--- /dev/null
+/*===================================================================
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright 2001 LSI Logic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version : v1.18d (Aug 7, 2002)
+ *
+ * Description: Linux device driver for LSI Logic MegaRAID controller
+ *
+ * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490
+ * 493.
+ * History:
+ *
+ * Version 0.90:
+ * Original source contributed by Dell; integrated it into the kernel and
+ * cleaned up some things. Added support for 438/466 controllers.
+ * Version 0.91:
+ * Aligned mailbox area on 16-byte boundary.
+ * Added schedule() at the end to properly clean up.
+ * Made improvements for conformity to linux driver standards.
+ *
+ * Version 0.92:
+ * Added support for 2.1 kernels.
+ * Reads from pci_dev struct, so it's not dependent on pcibios.
+ * Added some missing virt_to_bus() translations.
+ * Added support for SMP.
+ * Changed global cli()'s to spinlocks for 2.1, and simulated
+ * spinlocks for 2.0.
+ * Removed setting of SA_INTERRUPT flag when requesting Irq.
+ *
+ * Version 0.92ac:
+ * Small changes to the comments/formatting. Plus a couple of
+ * added notes. Returned to the authors. No actual code changes
+ * save printk levels.
+ * 8 Oct 98 Alan Cox <alan.cox@linux.org>
+ *
+ * Merged with 2.1.131 source tree.
+ * 12 Dec 98 K. Baranowski <kgb@knm.org.pl>
+ *
+ * Version 0.93:
+ * Added support for vendor specific ioctl commands (M_RD_IOCTL_CMD+xxh)
+ * Changed some fields in MEGARAID struct to better values.
+ * Added signature check for Rp controllers under 2.0 kernels
+ * Changed busy-wait loop to be time-based
+ * Fixed SMP race condition in isr
+ * Added kfree (sgList) on release
+ * Added #include linux/version.h to megaraid.h for hosts.h
+ * Changed max_id to represent max logical drives instead of targets.
+ *
+ * Version 0.94:
+ * Got rid of some excess locking/unlocking
+ * Fixed slight memory corruption problem while memcpy'ing into mailbox
+ * Changed logical drives to be reported as luns rather than targets
+ * Changed max_id to 16 since it is now max targets/chan again.
+ * Improved ioctl interface for upcoming megamgr
+ *
+ * Version 0.95:
+ * Fixed problem of queueing multiple commands to adapter;
+ * still has some strange problems on some setups, so still
+ * defaults to single. To enable parallel commands change
+ * #define MULTI_IO in megaraid.h
+ * Changed kmalloc allocation to be done in beginning.
+ * Got rid of C++ style comments
+ *
+ * Version 0.96:
+ * 762 fully supported.
+ *
+ * Version 0.97:
+ * Changed megaraid_command to use wait_queue.
+ *
+ * Version 1.00:
+ * Checks to see if an irq occurred while in isr, and runs through
+ * routine again.
+ * Copies mailbox to temp area before processing in isr
+ * Added barrier() in busy wait to fix volatility bug
+ * Uses separate list for freed Scbs, keeps track of cmd state
+ * Put spinlocks around entire queue function for now...
+ * Full multi-io commands working stablely without previous problems
+ * Added skipXX LILO option for Madrona motherboard support
+ *
+ * Version 1.01:
+ * Fixed bug in mega_cmd_done() for megamgr control commands,
+ * the host_byte in the result code from the scsi request to
+ * scsi midlayer is set to DID_BAD_TARGET when adapter's
+ * returned codes are 0xF0 and 0xF4.
+ *
+ * Version 1.02:
+ * Fixed the tape drive bug by extending the adapter timeout value
+ * for passthrough command to 60 seconds in mega_build_cmd().
+ *
+ * Version 1.03:
+ * Fixed Madrona support.
+ * Changed the adapter timeout value from 60 sec in 1.02 to 10 min
+ * for bigger and slower tape drive.
+ * Added driver version printout at driver loadup time
+ *
+ * Version 1.04
+ * Added code for 40 ld FW support.
+ * Added new ioctl command 0x81 to support NEW_READ/WRITE_CONFIG with
+ * data area greater than 4 KB, which is the upper bound for data
+ * tranfer through scsi_ioctl interface.
+ * The additional 32 bit field for 64bit address in the newly defined
+ * mailbox64 structure is set to 0 at this point.
+ *
+ * Version 1.05
+ * Changed the queing implementation for handling SCBs and completed
+ * commands.
+ * Added spinlocks in the interrupt service routine to enable the driver
+ * function in the SMP environment.
+ * Fixed the problem of unnecessary aborts in the abort entry point, which
+ * also enables the driver to handle large amount of I/O requests for
+ * long duration of time.
+ * Version 1.06
+ * Intel Release
+ * Version 1.07
+ * Removed the usage of uaccess.h file for kernel versions less than
+ * 2.0.36, as this file is not present in those versions.
+ *
+ * Version 108
+ * Modified mega_ioctl so that 40LD megamanager would run
+ * Made some changes for 2.3.XX compilation , esp wait structures
+ * Code merge between 1.05 and 1.06 .
+ * Bug fixed problem with ioctl interface for concurrency between
+ * 8ld and 40ld firwmare
+ * Removed the flawed semaphore logic for handling new config command
+ * Added support for building own scatter / gather list for big user
+ * mode buffers
+ * Added /proc file system support ,so that information is available in
+ * human readable format
+ *
+ * Version 1a08
+ * Changes for IA64 kernels. Checked for CONFIG_PROC_FS flag
+ *
+ * Version 1b08
+ * Include file changes.
+ * Version 1b08b
+ * Change PCI ID value for the 471 card, use #defines when searching
+ * for megaraid cards.
+ *
+ * Version 1.10
+ *
+ * I) Changes made to make following ioctl commands work in 0x81 interface
+ * a)DCMD_DELETE_LOGDRV
+ * b)DCMD_GET_DISK_CONFIG
+ * c)DCMD_DELETE_DRIVEGROUP
+ * d)NC_SUBOP_ENQUIRY3
+ * e)DCMD_CHANGE_LDNO
+ * f)DCMD_CHANGE_LOOPID
+ * g)DCMD_FC_READ_NVRAM_CONFIG
+ * h)DCMD_WRITE_CONFIG
+ * II) Added mega_build_kernel_sg function
+ * III)Firmware flashing option added
+ *
+ * Version 1.10a
+ *
+ * I)Dell updates included in the source code.
+ * Note: This change is not tested due to the unavailability of IA64 kernel
+ * and it is in the #ifdef DELL_MODIFICATION macro which is not defined
+ *
+ * Version 1.10b
+ *
+ * I)In M_RD_IOCTL_CMD_NEW command the wrong way of copying the data
+ * to the user address corrected
+ *
+ * Version 1.10c
+ *
+ * I) DCMD_GET_DISK_CONFIG opcode updated for the firmware changes.
+ *
+ * Version 1.11
+ * I) Version number changed from 1.10c to 1.11
+ * II) DCMD_WRITE_CONFIG(0x0D) command in the driver changed from
+ * scatter/gather list mode to direct pointer mode..
+ * Fixed bug of undesirably detecting HP onboard controllers which
+ * are disabled.
+ *
+ * Version 1.12 (Sep 21, 2000)
+ *
+ * I. Changes have been made for Dynamic DMA mapping in IA64 platform.
+ * To enable all these changes define M_RD_DYNAMIC_DMA_SUPPORT in megaraid.h
+ * II. Got rid of windows mode comments
+ * III. Removed unwanted code segments
+ * IV. Fixed bug of HP onboard controller information (commented with
+ * MEGA_HP_FIX)
+ *
+ * Version 1a12
+ * I. reboot notifier and new ioctl changes ported from 1c09
+ *
+ * Version 1b12
+ * I. Changes in new ioctl interface routines ( Nov 06, 2000 )
+ *
+ * Version 1c12
+ * I. Changes in new ioctl interface routines ( Nov 07, 2000 )
+ *
+ * Version 1d12
+ * I. Compilation error under kernel 2.4.0 for 32-bit machine in mega_ioctl
+ *
+ * Version 1e12, 1f12
+ * 1. Fixes for pci_map_single, pci_alloc_consistent along with mailbox
+ * alignment
+ *
+ * Version 1.13beta
+ * Added Support for Full 64bit address space support. If firmware
+ * supports 64bit, it goes to 64 bit mode even on x86 32bit
+ * systems. Data Corruption Issues while running on test9 kernel
+ * on IA64 systems. This issue not seen on test11 on x86 system
+ *
+ * Version 1.13c
+ * 1. Resolved Memory Leak when using M_RD_IOCTL_CMD interface
+ * 2. Resolved Queuing problem when MailBox Blocks
+ * 3. Added unregister_reboot_notifier support
+ *
+ * Version 1.13d
+ * Experimental changes in interfacing with the controller in ISR
+ *
+ * Version 1.13e
+ * Fixed Broken 2.2.XX compilation changes + misc changes
+ *
+ * Version 1.13f to 1.13i
+ * misc changes + code clean up
+ * Cleaned up the ioctl code and added set_mbox_xfer_addr()
+ * Support for START_DEV (6)
+ *
+ * Version 1.13j
+ * Moved some code to megaraid.h file, replaced some hard coded values
+ * with respective macros. Changed some functions to static
+ *
+ * Version 1.13k
+ * Only some idendation correction to 1.13j
+ *
+ * Version 1.13l , 1.13m, 1.13n, 1.13o
+ * Minor Identation changes + misc changes
+ *
+ * Version 1.13q
+ * Paded the new uioctl_t MIMD structure for maintaining alignment
+ * and size across 32 / 64 bit platforms
+ * Changed the way MIMD IOCTL interface used virt_to_bus() to use pci
+ * memory location
+ *
+ * Version 1.13r
+ * 2.4.xx SCSI Changes.
+ *
+ * Version 1.13s
+ * Stats counter fixes
+ * Temporary fix for some 64 bit firmwares in 2.4.XX kernels
+ *
+ * Version 1.13t
+ * Support for 64bit version of READ/WRITE/VIEW DISK CONFIG
+ *
+ * Version 1.14
+ * Did away with MEGADEV_IOCTL flag. It is now standard part of driver
+ * without need for a special #define flag
+ * Disabled old scsi ioctl path for kernel versions > 2.3.xx. This is due
+ * to the nature in which the new scsi code queues a new scsi command to
+ * controller during SCSI IO Completion
+ * Driver now checks for sub-system vendor id before taking ownership of
+ * the controller
+ *
+ * Version 1.14a
+ * Added Host re-ordering
+ *
+ * Version 1.14b
+ * Corrected some issue which caused the older cards not to work
+ *
+ * Version 1.14c
+ * IOCTL changes for not handling the non-64bit firmwares under 2.4.XX
+ * kernel
+ *
+ * Version 1.14d
+ * Fixed Various MIMD Synchronization Issues
+ *
+ * Version 1.14e
+ * Fixed the error handling during card initialization
+ *
+ * Version 1.14f
+ * Multiple invocations of mimd phase I ioctl stalls the cpu. Replaced
+ * spinlock with semaphore(mutex)
+ *
+ * Version 1.14g
+ * Fixed running out of scbs issues while running MIMD apps under heavy IO
+ *
+ * Version 1.14g-ac - 02/03/01
+ * Reformatted to Linux format so I could compare to old one and cross
+ * check bug fixes
+ * Re fixed the assorted missing 'static' cases
+ * Removed some unneeded version checks
+ * Cleaned up some of the VERSION checks in the code
+ * Left 2.0 support but removed 2.1.x support.
+ * Collected much of the compat glue into one spot
+ *
+ * Version 1.14g-ac2 - 22/03/01
+ * Fixed a non obvious dereference after free in the driver unload path
+ *
+ * Version 1.14i
+ * changes for making 32bit application run on IA64
+ *
+ * Version 1.14j
+ * Tue Mar 13 14:27:54 EST 2001 - AM
+ * Changes made in the driver to be able to run applications if the
+ * system has memory >4GB.
+ *
+ *
+ * Version 1.14k
+ * Thu Mar 15 18:38:11 EST 2001 - AM
+ *
+ * Firmware version check removed if subsysid==0x1111 and
+ * subsysvid==0x1111, since its not yet initialized.
+ *
+ * changes made to correctly calculate the base in mega_findCard.
+ *
+ * Driver informational messages now appear on the console as well as
+ * with dmesg
+ *
+ * Older ioctl interface is returned failure on newer(2.4.xx) kernels.
+ *
+ * Inclusion of "modversions.h" is still a debatable question. It is
+ * included anyway with this release.
+ *
+ * Version 1.14l
+ * Mon Mar 19 17:39:46 EST 2001 - AM
+ *
+ * Assorted changes to remove compilation error in 1.14k when compiled
+ * with kernel < 2.4.0
+ *
+ * Version 1.14m
+ * Tue Mar 27 12:09:22 EST 2001 - AM
+ *
+ * Added support for extended CDBs ( > 10 bytes ) and OBDR ( One Button
+ * Disaster Recovery ) feature.
+ *
+ *
+ * Version 1.14n
+ * Tue Apr 10 14:28:13 EDT 2001 - AM
+ *
+ * "modeversions.h" is no longer included in the code.
+ * 2.4.xx style mutex initialization used for older kernels also
+ *
+ * Version 1.14o
+ * Wed Apr 18 17:47:26 EDT 2001 - PJ
+ *
+ * Before returning status for 'inquiry', we first check if request buffer
+ * is SG list, and then return appropriate status
+ *
+ * Version 1.14p
+ * Wed Apr 25 13:44:48 EDT 2001 - PJ
+ *
+ * SCSI result made appropriate in case of check conditions for extended
+ * passthru commands
+ *
+ * Do not support lun >7 for physically accessed devices
+ *
+ *
+ * Version 1.15
+ * Thu Apr 19 09:38:38 EDT 2001 - AM
+ *
+ * 1.14l rollover to 1.15 - merged with main trunk after 1.15d
+ *
+ * Version 1.15b
+ * Wed May 16 20:10:01 EDT 2001 - AM
+ *
+ * "modeversions.h" is no longer included in the code.
+ * 2.4.xx style mutex initialization used for older kernels also
+ * Brought in-sync with Alan's changes in 2.4.4
+ * Note: 1.15a is on OBDR branch(main trunk), and is not merged with yet.
+ *
+ * Version 1.15c
+ * Mon May 21 23:10:42 EDT 2001 - AM
+ *
+ * ioctl interface uses 2.4.x conforming pci dma calls
+ * similar calls used for older kernels
+ *
+ * Version 1.15d
+ * Wed May 30 17:30:41 EDT 2001 - AM
+ *
+ * NULL is not a valid first argument for pci_alloc_consistent() on
+ * IA64(2.4.3-2.10.1). Code shuffling done in ioctl interface to get
+ * "pci_dev" before making calls to pci interface routines.
+ *
+ * Version 1.16pre
+ * Fri Jun 1 19:40:48 EDT 2001 - AM
+ *
+ * 1.14p and 1.15d merged
+ * ROMB support added
+ *
+ * Version 1.16-pre1
+ * Mon Jun 4 15:01:01 EDT 2001 - AM
+ *
+ * Non-ROMB firmware do no DMA support 0xA9 command. Value 0xFF
+ * (all channels are raid ) is chosen for those firmware.
+ *
+ * Version 1.16-pre2
+ * Mon Jun 11 18:15:31 EDT 2001 - AM
+ *
+ * Changes for boot from any logical drive
+ *
+ * Version 1.16
+ * Tue Jun 26 18:07:02 EDT 2001 - AM
+ *
+ * branched at 1.14p
+ *
+ * Check added for HP 1M/2M controllers if having firmware H.01.07 or
+ * H.01.08. If found, disable 64 bit support since these firmware have
+ * limitations for 64 bit addressing
+ *
+ *
+ * Version 1.17
+ * Thu Jul 12 11:14:09 EDT 2001 - AM
+ *
+ * 1.16pre2 and 1.16 merged.
+ *
+ * init_MUTEX and init_MUTEX_LOCKED are defined in 2.2.19. Pre-processor
+ * statements are added for them
+ *
+ * Linus's 2.4.7pre3 kernel introduces a new field 'max_sectors' in Scsi_Host
+ * structure, to improve IO performance.
+ *
+ *
+ * Version 1.17a
+ * Fri Jul 13 18:44:01 EDT 2001 - AM
+ *
+ * Starting from kernel 2.4.x, LUN is not < 8 - following SCSI-III. So to have
+ * our current formula working to calculate logical drive number, return
+ * failure for LUN > 7
+ *
+ *
+ * Version 1.17b
+ * Mon Jul 30 19:24:02 EDT 2001 - AM
+ *
+ * Added support for random deletion of logical drives
+ *
+ * Version 1.17c
+ * Tue Sep 25 09:37:49 EDT 2001 - Atul Mukker <atulm@lsil.com>
+ *
+ * With single and dual channel controllers, some virtaul channels are
+ * displayed negative.
+ *
+ * Version 1.17a-ac
+ * Mon Aug 6 14:59:29 BST 2001 - "Michael Johnson" <johnsom@home.com>
+ *
+ * Make the HP print formatting and check for buggy firmware runtime not
+ * ifdef dependant.
+ *
+ *
+ * Version 1.17d
+ * Thu Oct 11 10:48:45 EDT 2001 - Atul Mukker <atulm@lsil.com>
+ *
+ * Driver 1.17c oops when loaded without controller.
+ *
+ * Special case for "use_sg == 1" removed while building the scatter gather
+ * list.
+ *
+ * Version 1.18
+ * Thu Oct 11 15:02:53 EDT 2001 - Atul Mukker <atulm@lsil.com>
+ *
+ * References to AMI have been changed to LSI Logic.
+ *
+ * Version 1.18a
+ * Mon Mar 11 11:38:38 EST 2002 - Atul Mukker <Atul.Mukker@lsil.com>
+ *
+ * RAID On MotherBoard (ROMB) - boot from logical or physical drives
+ *
+ * Support added for discovery(ROMB) vendor and device ids.
+ *
+ * Data transfer length for passthru commands must be valid even if the
+ * command has an associated scatter-gather list.
+ *
+ *
+ * Version 1.18b
+ * Tue Apr 23 11:01:58 EDT 2002 - Atul Mukker <Atul.Mukker@lsil.com>
+ *
+ * typo corrected for scsi condition CHECK_CONDITION in mega_cmd_done()
+ *
+ * Support added for PCI_VENDOR_ID_LSI_LOGIC with device id
+ * PCI_DEVICE_ID_AMI_MEGARAID3.
+ *
+ *
+ * Version 1.18c
+ * Thu May 16 10:27:55 EDT 2002 - Atul Mukker <Atul.Mukker@lsil.com>
+ *
+ * Retrun valid return status for mega passthru commands resulting in
+ * contingent allegiance condition. Check for 64-bit passthru commands also.
+ *
+ * Do not check_region() anymore and check for return value of
+ * request_region()
+ *
+ * Send valid sense data to appliations using the private ioctl interface.
+ *
+ *
+ * Version 1.18d
+ * Wed Aug 7 18:51:51 EDT 2002 - Atul Mukker <atul.mukker@lsil.com>
+ *
+ * Added support for yellowstone and verde controller
+ *
+ *
+ * BUGS:
+ * Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that
+ * fails to detect the controller as a pci device on the system.
+ *
+ * Timeout period for upper scsi layer, i.e. SD_TIMEOUT in
+ * /drivers/scsi/sd.c, is too short for this controller. SD_TIMEOUT
+ * value must be increased to (30 * HZ) otherwise false timeouts
+ * will occur in the upper layer.
+ *
+ * Never set skip_id. The existing PCI code the megaraid uses fails
+ * to properly check the vendor subid in some cases. Setting this then
+ * makes it steal other i960's and crashes some boxes
+ *
+ * Far too many ifdefs for versions.
+ *
+ *===================================================================*/
+
+
+#include <xeno/config.h>
+#include <xeno/version.h>
+#include <xeno/module.h>
+#include <xeno/types.h>
+#include <xeno/errno.h>
+#include <xeno/string.h>
+#include <xeno/lib.h>
+#include <xeno/kernel.h>
+#include <xeno/ioport.h>
+//#include <xeno/fcntl.h>
+#include <xeno/delay.h>
+#include <xeno/pci.h>
+//#include <xeno/proc_fs.h>
+#include <xeno/blk.h>
+//#include <xeno/wait.h>
+#include <xeno/tqueue.h>
+#include <xeno/interrupt.h>
+#include <xeno/mm.h>
+//#include <asm/pgtable.h>
+
+#include <asm/ioctl.h>
+#include <xeno/sched.h>
+//#include <xeno/stat.h>
+#include <xeno/slab.h> /* for kmalloc() */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */
+#include <xeno/bios32.h>
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /* 0x20300 */
+#include <asm/spinlock.h>
+#else
+#include <xeno/spinlock.h>
+#endif
+#endif
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /* 0x020024 */
+#include <asm/uaccess.h>
+#endif
+
+/*
+ * These header files are required for Shutdown Notification routines
+ */
+#include <xeno/notifier.h>
+#include <xeno/reboot.h>
+#include <xeno/init.h>
+
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+
+#include "megaraid.h"
+
+/*
+ *================================================================
+ * #Defines
+ *================================================================
+ */
+
+#define MAX_SERBUF 160
+#define COM_BASE 0x2f8
+
+static ulong RDINDOOR (mega_host_config * megaCfg)
+{
+ return readl (megaCfg->base + 0x20);
+}
+
+static void WRINDOOR (mega_host_config * megaCfg, ulong value)
+{
+ writel (value, megaCfg->base + 0x20);
+}
+
+static ulong RDOUTDOOR (mega_host_config * megaCfg)
+{
+ return readl (megaCfg->base + 0x2C);
+}
+
+static void WROUTDOOR (mega_host_config * megaCfg, ulong value)
+{
+ writel (value, megaCfg->base + 0x2C);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */
+#include <xeno/smp.h>
+#define cpuid smp_processor_id()
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+#define scsi_set_pci_device(x,y)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */
+
+/*
+ * Linux 2.4 and higher
+ *
+ * No driver private lock
+ * Use the io_request_lock not cli/sti
+ * queue task is a simple api without irq forms
+ */
+
+MODULE_AUTHOR ("LSI Logic Corporation");
+MODULE_DESCRIPTION ("LSI Logic MegaRAID driver");
+MODULE_LICENSE ("GPL");
+
+#define DRIVER_LOCK_T
+#define DRIVER_LOCK_INIT(p)
+#define DRIVER_LOCK(p)
+#define DRIVER_UNLOCK(p)
+#define IO_LOCK_T unsigned long io_flags = 0
+#define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags);
+#define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags);
+
+#define queue_task_irq(a,b) queue_task(a,b)
+#define queue_task_irq_off(a,b) queue_task(a,b)
+
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */
+
+/*
+ * Linux 2.2 and higher
+ *
+ * No driver private lock
+ * Use the io_request_lock not cli/sti
+ * No pci region api
+ * queue_task is now a single simple API
+ */
+
+static char kernel_version[] = UTS_RELEASE;
+MODULE_AUTHOR ("LSI Logic Corporation");
+MODULE_DESCRIPTION ("LSI Logic MegaRAID driver");
+
+#define DRIVER_LOCK_T
+#define DRIVER_LOCK_INIT(p)
+#define DRIVER_LOCK(p)
+#define DRIVER_UNLOCK(p)
+#define IO_LOCK_T unsigned long io_flags = 0
+#define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags);
+#define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags);
+
+#define pci_free_consistent(a,b,c,d)
+#define pci_unmap_single(a,b,c,d)
+#define pci_enable_device(x) (0)
+#define queue_task_irq(a,b) queue_task(a,b)
+#define queue_task_irq_off(a,b) queue_task(a,b)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */
+#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED)
+#define init_MUTEX(x) (*(x)=MUTEX)
+#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+#endif
+
+
+#else
+
+/*
+ * Linux 2.0 macros. Here we have to provide some of our own
+ * functionality. We also only work little endian 32bit.
+ * Again no pci_alloc/free api
+ * IO_LOCK/IO_LOCK_T were never used in 2.0 so now are empty
+ */
+
+#define cpuid 0
+#define DRIVER_LOCK_T long cpu_flags;
+#define DRIVER_LOCK_INIT(p)
+#define DRIVER_LOCK(p) \
+ save_flags(cpu_flags); \
+ cli();
+#define DRIVER_UNLOCK(p) \
+ restore_flags(cpu_flags);
+#define IO_LOCK_T
+#define IO_LOCK(p)
+#define IO_UNLOCK(p)
+#define le32_to_cpu(x) (x)
+#define cpu_to_le32(x) (x)
+
+#define pci_free_consistent(a,b,c,d)
+#define pci_unmap_single(a,b,c,d)
+
+#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED)
+#define init_MUTEX(x) (*(x)=MUTEX)
+
+#define pci_enable_device(x) (0)
+
+/*
+ * 2.0 lacks spinlocks, iounmap/ioremap
+ */
+
+#define ioremap vremap
+#define iounmap vfree
+
+ /* simulate spin locks */
+typedef struct {
+ volatile char lock;
+} spinlock_t;
+
+#define spin_lock_init(x) { (x)->lock = 0;}
+#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\
+ (x)->lock=1; save_flags(flags);\
+ cli();}
+#define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);}
+
+#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+
+#endif
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */
+#define dma_alloc_consistent pci_alloc_consistent
+#define dma_free_consistent pci_free_consistent
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */
+typedef unsigned long dma_addr_t;
+#endif
+void *dma_alloc_consistent(void *, size_t, dma_addr_t *);
+void dma_free_consistent(void *, size_t, void *, dma_addr_t);
+int mega_get_order(int);
+int pow_2(int);
+#endif
+
+/* set SERDEBUG to 1 to enable serial debugging */
+#define SERDEBUG 0
+#if SERDEBUG
+static void ser_init (void);
+static void ser_puts (char *str);
+static void ser_putc (char c);
+static int ser_printk (const char *fmt, ...);
+#endif
+
+#ifdef CONFIG_PROC_FS
+#define COPY_BACK if (offset > megaCfg->procidx) { \
+ *eof = TRUE; \
+ megaCfg->procidx = 0; \
+ megaCfg->procbuf[0] = 0; \
+ return 0;} \
+ if ((count + offset) > megaCfg->procidx) { \
+ count = megaCfg->procidx - offset; \
+ *eof = TRUE; } \
+ memcpy(page, &megaCfg->procbuf[offset], count); \
+ megaCfg->procidx = 0; \
+ megaCfg->procbuf[0] = 0;
+#endif
+
+/*
+ * ================================================================
+ * Global variables
+ *================================================================
+ */
+
+/* Use "megaraid=skipXX" as LILO option to prohibit driver from scanning
+ XX scsi id on each channel. Used for Madrona motherboard, where SAF_TE
+ processor id cannot be scanned */
+
+static char *megaraid;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) /* 0x20100 */
+#ifdef MODULE
+MODULE_PARM (megaraid, "s");
+#endif
+#endif
+static int skip_id = -1;
+static int numCtlrs = 0;
+static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = { 0 };
+#if XENO_KILLED
+static struct proc_dir_entry *mega_proc_dir_entry;
+#endif
+
+#if DEBUG
+static u32 maxCmdTime = 0;
+#endif
+
+static mega_scb *pLastScb = NULL;
+#if XENO_KILLED
+static struct notifier_block mega_notifier = {
+ megaraid_reboot_notify,
+ NULL,
+ 0
+};
+#endif
+
+/* For controller re-ordering */
+struct mega_hbas mega_hbas[MAX_CONTROLLERS];
+
+/*
+ * The File Operations structure for the serial/ioctl interface of the driver
+ */
+/* For controller re-ordering */
+#if XENO_KILLED
+static struct file_operations megadev_fops = {
+ ioctl:megadev_ioctl_entry,
+ open:megadev_open,
+ release:megadev_close,
+};
+#endif
+/*
+ * Array to structures for storing the information about the controllers. This
+ * information is sent to the user level applications, when they do an ioctl
+ * for this information.
+ */
+static struct mcontroller mcontroller[MAX_CONTROLLERS];
+
+/* The current driver version */
+static u32 driver_ver = 0x118C;
+
+#if XENO_KILLED
+/* major number used by the device for character interface */
+static int major;
+
+static struct semaphore mimd_ioctl_sem;
+static struct semaphore mimd_entry_mtx;
+#endif
+
+#if SERDEBUG
+volatile static spinlock_t serial_lock;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x20300 */
+static struct proc_dir_entry proc_scsi_megaraid = {
+ PROC_SCSI_MEGARAID, 8, "megaraid",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry proc_root;
+#endif
+
+#define IS_RAID_CH(this, ch) ( (this->mega_ch_class >> (ch)) & 0x01 )
+
+#if SERDEBUG
+static char strbuf[MAX_SERBUF + 1];
+
+static void ser_init (void)
+{
+ unsigned port = COM_BASE;
+
+ outb (0x80, port + 3);
+ outb (0, port + 1);
+ /* 9600 Baud, if 19200: outb(6,port) */
+ outb (12, port);
+ outb (3, port + 3);
+ outb (0, port + 1);
+}
+
+static void ser_puts (char *str)
+{
+ char *ptr;
+
+ ser_init ();
+ for (ptr = str; *ptr; ++ptr)
+ ser_putc (*ptr);
+}
+
+static void ser_putc (char c)
+{
+ unsigned port = COM_BASE;
+
+ while ((inb (port + 5) & 0x20) == 0) ;
+ outb (c, port);
+ if (c == 0x0a) {
+ while ((inb (port + 5) & 0x20) == 0) ;
+ outb (0x0d, port);
+ }
+}
+
+static int ser_printk (const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ long flags;
+
+ spin_lock_irqsave (&serial_lock, flags);
+ va_start (args, fmt);
+ i = vsprintf (strbuf, fmt, args);
+ ser_puts (strbuf);
+ va_end (args);
+ spin_unlock_irqrestore (&serial_lock, flags);
+
+ return i;
+}
+
+#define TRACE(a) { ser_printk a;}
+
+#else
+#define TRACE(A)
+#endif
+
+#define TRACE1(a)
+
+static void callDone (Scsi_Cmnd * SCpnt)
+{
+ if (SCpnt->result) {
+ TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n",
+ SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel,
+ SCpnt->target, SCpnt->lun, SCpnt->result));
+ }
+ SCpnt->scsi_done (SCpnt);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Local functions
+ *
+ *-------------------------------------------------------------------------*/
+
+/*=======================
+ * Free a SCB structure
+ *=======================
+ */
+static void mega_freeSCB (mega_host_config * megaCfg, mega_scb * pScb)
+{
+
+ mega_scb *pScbtmp;
+
+ if ((pScb == NULL) || (pScb->idx >= 0xFE)) {
+ return;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ switch (pScb->dma_type) {
+ case M_RD_DMA_TYPE_NONE:
+ break;
+ case M_RD_PTHRU_WITH_BULK_DATA:
+ pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata,
+ pScb->pthru->dataxferlen,
+ pScb->dma_direction);
+ break;
+ case M_RD_EPTHRU_WITH_BULK_DATA:
+ pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata,
+ pScb->epthru->dataxferlen,
+ pScb->dma_direction);
+ break;
+ case M_RD_PTHRU_WITH_SGLIST:
+ {
+ int count;
+ for (count = 0; count < pScb->sglist_count; count++) {
+ pci_unmap_single (megaCfg->dev,
+ pScb->dma_h_sglist[count],
+ pScb->sgList[count].length,
+ pScb->dma_direction);
+
+ }
+ break;
+ }
+ case M_RD_BULK_DATA_ONLY:
+ pci_unmap_single (megaCfg->dev,
+ pScb->dma_h_bulkdata,
+ pScb->iDataSize, pScb->dma_direction);
+
+ break;
+ case M_RD_SGLIST_ONLY:
+ pci_unmap_sg (megaCfg->dev,
+ pScb->SCpnt->request_buffer,
+ pScb->SCpnt->use_sg, pScb->dma_direction);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ /* Unlink from pending queue */
+ if (pScb == megaCfg->qPendingH) {
+
+ if (megaCfg->qPendingH == megaCfg->qPendingT)
+ megaCfg->qPendingH = megaCfg->qPendingT = NULL;
+ else
+ megaCfg->qPendingH = megaCfg->qPendingH->next;
+
+ megaCfg->qPcnt--;
+
+ } else {
+ for (pScbtmp = megaCfg->qPendingH; pScbtmp;
+ pScbtmp = pScbtmp->next) {
+
+ if (pScbtmp->next == pScb) {
+
+ pScbtmp->next = pScb->next;
+
+ if (pScb == megaCfg->qPendingT) {
+ megaCfg->qPendingT = pScbtmp;
+ }
+
+ megaCfg->qPcnt--;
+ break;
+ }
+ }
+ }
+
+ /* Link back into free list */
+ pScb->state = SCB_FREE;
+ pScb->SCpnt = NULL;
+
+ if (megaCfg->qFreeH == (mega_scb *) NULL) {
+ megaCfg->qFreeH = megaCfg->qFreeT = pScb;
+ } else {
+ megaCfg->qFreeT->next = pScb;
+ megaCfg->qFreeT = pScb;
+ }
+
+ megaCfg->qFreeT->next = NULL;
+ megaCfg->qFcnt++;
+
+}
+
+/*===========================
+ * Allocate a SCB structure
+ *===========================
+ */
+static mega_scb *mega_allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
+{
+ mega_scb *pScb;
+
+ /* Unlink command from Free List */
+ if ((pScb = megaCfg->qFreeH) != NULL) {
+ megaCfg->qFreeH = pScb->next;
+ megaCfg->qFcnt--;
+
+ pScb->isrcount = jiffies;
+ pScb->next = NULL;
+ pScb->state = SCB_ACTIVE;
+ pScb->SCpnt = SCpnt;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pScb->dma_type = M_RD_DMA_TYPE_NONE;
+#endif
+
+ return pScb;
+ }
+
+ printk (KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n");
+
+ return NULL;
+}
+
+/* Run through the list of completed requests and finish it */
+static void mega_rundoneq (mega_host_config * megaCfg)
+{
+ Scsi_Cmnd *SCpnt;
+
+ while ((SCpnt = megaCfg->qCompletedH) != NULL) {
+ megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble;
+ megaCfg->qCcnt--;
+
+ SCpnt->host_scribble = (unsigned char *) NULL; /* XC : sep 14 */
+ /* Callback */
+ callDone (SCpnt);
+ }
+
+ megaCfg->qCompletedH = megaCfg->qCompletedT = NULL;
+}
+
+/*
+ * Runs through the list of pending requests
+ * Assumes that mega_lock spin_lock has been acquired.
+ */
+static int mega_runpendq (mega_host_config * megaCfg)
+{
+ mega_scb *pScb;
+ int rc;
+
+ /* Issue any pending commands to the card */
+ for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) {
+ if (pScb->state == SCB_ACTIVE) {
+ if ((rc =
+ megaIssueCmd (megaCfg, pScb->mboxData, pScb, 1)) == -1)
+ return rc;
+ }
+ }
+ return 0;
+}
+
+/* Add command to the list of completed requests */
+
+static void mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, int status)
+{
+ int islogical;
+ Scsi_Cmnd *SCpnt;
+ mega_passthru *pthru;
+ mega_ext_passthru *epthru;
+ mega_mailbox *mbox;
+ struct scatterlist *sgList;
+ u8 c;
+
+ if (pScb == NULL) {
+ TRACE (("NULL pScb in mega_cmd_done!"));
+ printk(KERN_CRIT "NULL pScb in mega_cmd_done!");
+ }
+
+ SCpnt = pScb->SCpnt;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pthru = pScb->pthru;
+ epthru = pScb->epthru;
+#else
+ pthru = &pScb->pthru;
+ epthru = &pScb->epthru;
+#endif
+
+ mbox = (mega_mailbox *) & pScb->mboxData;
+
+ if (SCpnt == NULL) {
+ TRACE (("NULL SCpnt in mega_cmd_done!"));
+ TRACE (("pScb->idx = ", pScb->idx));
+ TRACE (("pScb->state = ", pScb->state));
+ TRACE (("pScb->state = ", pScb->state));
+ panic(KERN_ERR "megaraid:Problem...!\n");
+ }
+
+#if 0
+ islogical = ( (SCpnt->channel >= megaCfg->productInfo.SCSIChanPresent) &&
+ (SCpnt->channel <= megaCfg->host->max_channel) );
+#endif
+#if 0
+ islogical = (SCpnt->channel == megaCfg->host->max_channel);
+#endif
+ islogical = megaCfg->logdrv_chan[SCpnt->channel];
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* Special Case to handle PassThrough->XferAddrress > 4GB */
+ switch (SCpnt->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+ memcpy (SCpnt->request_buffer,
+ pScb->bounce_buffer, SCpnt->request_bufflen);
+ break;
+ }
+#endif
+
+ mega_freeSCB (megaCfg, pScb);
+
+ /*
+ * Do not return the presence of hard disk on the channel so, inquiry
+ * sent, and returned data==hard disk or removable hard disk and not
+ * logical, request should return failure! - PJ
+ */
+#if 0
+ if (SCpnt->cmnd[0] == INQUIRY && ((((u_char *) SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) && !islogical) {
+ status = 0xF0;
+ }
+#endif
+ if (SCpnt->cmnd[0] == INQUIRY && !islogical) {
+ if ( SCpnt->use_sg ) {
+ sgList = (struct scatterlist *)SCpnt->request_buffer;
+ memcpy(&c, sgList[0].address, 0x1);
+ } else {
+ memcpy(&c, SCpnt->request_buffer, 0x1);
+ }
+#if 0
+ if( (c & 0x1F ) == TYPE_DISK ) {
+ status = 0xF0;
+ }
+#endif
+ if(IS_RAID_CH(megaCfg, SCpnt->channel) && ((c & 0x1F) == TYPE_DISK)) {
+ status = 0xF0;
+ }
+ }
+
+
+ /* clear result; otherwise, success returns corrupt value */
+ SCpnt->result = 0;
+
+ if ( 0 && SCpnt->cmnd[0] & M_RD_IOCTL_CMD ) { /* i.e. ioctl cmd such as M_RD_IOCTL_CMD, M_RD_IOCTL_CMD_NEW of megamgr */
+ switch (status) {
+ case 2:
+ case 0xF0:
+ case 0xF4:
+ SCpnt->result = (DID_BAD_TARGET << 16) | status;
+ break;
+ default:
+ SCpnt->result |= status;
+ } /*end of switch */
+ } else {
+ /* Convert MegaRAID status to Linux error code */
+ switch (status) {
+ case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */
+ SCpnt->result |= (DID_OK << 16);
+ break;
+
+ case 0x02: /* ERROR_ABORTED, i.e. SCSI_STATUS_CHECK_CONDITION */
+
+ /*set sense_buffer and result fields */
+ if (mbox->cmd == MEGA_MBOXCMD_PASSTHRU || mbox->cmd ==
+ MEGA_MBOXCMD_PASSTHRU64 ) {
+
+ memcpy (SCpnt->sense_buffer, pthru->reqsensearea, 14);
+
+ SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) |
+ (CHECK_CONDITION << 1);
+
+ } else if (mbox->cmd == MEGA_MBOXCMD_EXTPASSTHRU) {
+
+ memcpy( SCpnt->sense_buffer, epthru->reqsensearea, 14);
+
+ SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) |
+ (CHECK_CONDITION << 1);
+
+ } else {
+ SCpnt->sense_buffer[0] = 0x70;
+ SCpnt->sense_buffer[2] = ABORTED_COMMAND;
+ SCpnt->result |= (CHECK_CONDITION << 1);
+ }
+ break;
+
+ case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e. SCSI_STATUS_BUSY */
+ SCpnt->result |= (DID_BUS_BUSY << 16) | status;
+ break;
+
+ default:
+ SCpnt->result |= (DID_BAD_TARGET << 16) | status;
+ break;
+ }
+ }
+
+ /* Add Scsi_Command to end of completed queue */
+ if (megaCfg->qCompletedH == NULL) {
+ megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt;
+ } else {
+ megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt;
+ megaCfg->qCompletedT = SCpnt;
+ }
+
+ megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL;
+ megaCfg->qCcnt++;
+}
+
+/*-------------------------------------------------------------------
+ *
+ * Build a SCB from a Scsi_Cmnd
+ *
+ * Returns a SCB pointer, or NULL
+ * If NULL is returned, the scsi_done function MUST have been called
+ *
+ *-------------------------------------------------------------------*/
+
+static mega_scb *mega_build_cmd (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
+{
+ mega_scb *pScb;
+ mega_mailbox *mbox;
+ mega_passthru *pthru;
+ mega_ext_passthru *epthru;
+ long seg;
+ char islogical;
+ int max_ldrv_num;
+ int channel = 0;
+ int target = 0;
+ int ldrv_num = 0; /* logical drive number */
+
+ if ((SCpnt->cmnd[0] == MEGADEVIOC))
+ return megadev_doioctl (megaCfg, SCpnt);
+
+ if ((SCpnt->cmnd[0] == M_RD_IOCTL_CMD)
+ || (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW))
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+ return mega_ioctl (megaCfg, SCpnt); /* Handle IOCTL command */
+#else
+ {
+ printk(KERN_WARNING "megaraid ioctl: older interface - "
+ "not supported.\n");
+ return NULL;
+ }
+#endif
+
+#if 0
+ islogical = ( (SCpnt->channel >= megaCfg->productInfo.SCSIChanPresent) &&
+ (SCpnt->channel <= megaCfg->host->max_channel) );
+#endif
+#if 0
+ islogical = (IS_RAID_CH(SCpnt->channel) && /* virtual ch is raid - AM */
+ (SCpnt->channel == megaCfg->host->max_channel));
+#endif
+
+ /*
+ * We know on what channels are our logical drives - mega_findCard()
+ */
+ islogical = megaCfg->logdrv_chan[SCpnt->channel];
+
+ /*
+ * The theory: If physical drive is chosen for boot, all the physical
+ * devices are exported before the logical drives, otherwise physical
+ * devices are pushed after logical drives, in which case - Kernel sees
+ * the physical devices on virtual channel which is obviously converted
+ * to actual channel on the HBA.
+ */
+ if( megaCfg->boot_pdrv_enabled ) {
+ if( islogical ) {
+ /* logical channel */
+ channel = SCpnt->channel - megaCfg->productInfo.SCSIChanPresent;
+ }
+ else {
+ channel = SCpnt->channel; /* this is physical channel */
+ target = SCpnt->target;
+
+ /*
+ * boot from a physical disk, that disk needs to be exposed first
+ * IF both the channels are SCSI, then booting from the second
+ * channel is not allowed.
+ */
+ if( target == 0 ) {
+ target = megaCfg->boot_pdrv_tgt;
+ }
+ else if( target == megaCfg->boot_pdrv_tgt ) {
+ target = 0;
+ }
+ }
+ }
+ else {
+ if( islogical ) {
+ channel = SCpnt->channel; /* this is the logical channel */
+ }
+ else {
+ channel = SCpnt->channel - NVIRT_CHAN; /* physical channel */
+ target = SCpnt->target;
+ }
+ }
+
+ if ( ! megaCfg->support_ext_cdb ) {
+ if (!islogical && SCpnt->lun != 0) {
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+ }
+
+ if (!islogical && SCpnt->target == skip_id) {
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+
+ if (islogical) {
+
+ /* have just LUN 0 for each target on virtual channels */
+ if( SCpnt->lun != 0 ) {
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+
+ ldrv_num = mega_get_ldrv_num(megaCfg, SCpnt, channel);
+
+ max_ldrv_num = (megaCfg->flag & BOARD_40LD) ?
+ FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES;
+
+ /*
+ * max_ldrv_num increases by 0x80 if some logical drive was deleted.
+ */
+ if(megaCfg->read_ldidmap) {
+ max_ldrv_num += 0x80;
+ }
+
+ if( ldrv_num > max_ldrv_num ) {
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+
+ } else {
+ if ( SCpnt->lun > 7) {
+ /* Do not support lun >7 for physically accessed devices */
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+ }
+ /*-----------------------------------------------------
+ *
+ * Logical drive commands
+ *
+ *-----------------------------------------------------*/
+ if (islogical) {
+ switch (SCpnt->cmnd[0]) {
+ case TEST_UNIT_READY:
+ memset (SCpnt->request_buffer, 0, SCpnt->request_bufflen);
+ SCpnt->result = (DID_OK << 16);
+ callDone (SCpnt);
+ return NULL;
+
+ case MODE_SENSE:
+ memset (SCpnt->request_buffer, 0, SCpnt->cmnd[4]);
+ SCpnt->result = (DID_OK << 16);
+ callDone (SCpnt);
+ return NULL;
+
+ case READ_CAPACITY:
+ case INQUIRY:
+ if(!(megaCfg->flag & (1L << SCpnt->channel))) {
+ printk(KERN_NOTICE
+ "scsi%d: scanning virtual channel %d for logical drives.\n",
+ megaCfg->host->host_no, channel);
+
+ megaCfg->flag |= (1L << SCpnt->channel);
+ }
+
+ /* Allocate a SCB and initialize passthru */
+ if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) {
+ SCpnt->result = (DID_ERROR << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pthru = pScb->pthru;
+#else
+ pthru = &pScb->pthru;
+#endif
+
+ mbox = (mega_mailbox *) & pScb->mboxData;
+ memset (mbox, 0, sizeof (pScb->mboxData));
+ memset (pthru, 0, sizeof (mega_passthru));
+ pthru->timeout = 0;
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 1;
+ pthru->logdrv = ldrv_num;
+ pthru->cdblen = SCpnt->cmd_len;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /*Not sure about the direction */
+ pScb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+ pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA;
+
+#if 0
+/* Normal Code w/o the need for bounce buffer */
+ pScb->dma_h_bulkdata
+ = pci_map_single (megaCfg->dev,
+ SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ pScb->dma_direction);
+
+ pthru->dataxferaddr = pScb->dma_h_bulkdata;
+#else
+/* Special Code to use bounce buffer for READ_CAPA/INQ */
+ pthru->dataxferaddr = pScb->dma_bounce_buffer;
+ pScb->dma_type = M_RD_DMA_TYPE_NONE;
+#endif
+
+#else
+ pthru->dataxferaddr =
+ virt_to_bus (SCpnt->request_buffer);
+#endif
+
+ pthru->dataxferlen = SCpnt->request_bufflen;
+ memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ /* Initialize mailbox area */
+ mbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ mbox->xferaddr = pScb->dma_passthruhandle64;
+ TRACE1 (("M_RD_PTHRU_WITH_BULK_DATA Enabled \n"));
+#else
+ mbox->xferaddr = virt_to_bus (pthru);
+#endif
+ return pScb;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ /* Allocate a SCB and initialize mailbox */
+ if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) {
+ SCpnt->result = (DID_ERROR << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+ mbox = (mega_mailbox *) & pScb->mboxData;
+
+ memset (mbox, 0, sizeof (pScb->mboxData));
+ mbox->logdrv = ldrv_num;
+
+ if (megaCfg->flag & BOARD_64BIT) {
+ mbox->cmd = (*SCpnt->cmnd == READ_6
+ || *SCpnt->cmnd ==
+ READ_10) ? MEGA_MBOXCMD_LREAD64 :
+ MEGA_MBOXCMD_LWRITE64;
+ } else {
+ mbox->cmd = (*SCpnt->cmnd == READ_6
+ || *SCpnt->cmnd ==
+ READ_10) ? MEGA_MBOXCMD_LREAD :
+ MEGA_MBOXCMD_LWRITE;
+ }
+
+ /* 6-byte */
+ if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) {
+ mbox->numsectors = (u32) SCpnt->cmnd[4];
+ mbox->lba =
+ ((u32) SCpnt->cmnd[1] << 16) |
+ ((u32) SCpnt->cmnd[2] << 8) |
+ (u32) SCpnt->cmnd[3];
+ mbox->lba &= 0x1FFFFF;
+
+ if (*SCpnt->cmnd == READ_6) {
+ megaCfg->nReads[(int)ldrv_num]++;
+ megaCfg->nReadBlocks[(int)ldrv_num] +=
+ mbox->numsectors;
+ } else {
+ megaCfg->nWrites[(int)ldrv_num]++;
+ megaCfg->nWriteBlocks[(int)ldrv_num] +=
+ mbox->numsectors;
+ }
+ }
+
+ /* 10-byte */
+ if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) {
+ mbox->numsectors =
+ (u32) SCpnt->cmnd[8] |
+ ((u32) SCpnt->cmnd[7] << 8);
+ mbox->lba =
+ ((u32) SCpnt->cmnd[2] << 24) |
+ ((u32) SCpnt->cmnd[3] << 16) |
+ ((u32) SCpnt->cmnd[4] << 8) |
+ (u32) SCpnt->cmnd[5];
+
+ if (*SCpnt->cmnd == READ_10) {
+ megaCfg->nReads[(int)ldrv_num]++;
+ megaCfg->nReadBlocks[(int)ldrv_num] +=
+ mbox->numsectors;
+ } else {
+ megaCfg->nWrites[(int)ldrv_num]++;
+ megaCfg->nWriteBlocks[(int)ldrv_num] +=
+ mbox->numsectors;
+ }
+ }
+
+ /* 12-byte */
+ if (*SCpnt->cmnd == READ_12 || *SCpnt->cmnd == WRITE_12) {
+ mbox->lba =
+ ((u32) SCpnt->cmnd[2] << 24) |
+ ((u32) SCpnt->cmnd[3] << 16) |
+ ((u32) SCpnt->cmnd[4] << 8) |
+ (u32) SCpnt->cmnd[5];
+
+ mbox->numsectors =
+ ((u32) SCpnt->cmnd[6] << 24) |
+ ((u32) SCpnt->cmnd[7] << 16) |
+ ((u32) SCpnt->cmnd[8] << 8) |
+ (u32) SCpnt->cmnd[9];
+
+ if (*SCpnt->cmnd == READ_12) {
+ megaCfg->nReads[(int)ldrv_num]++;
+ megaCfg->nReadBlocks[(int)ldrv_num] +=
+ mbox->numsectors;
+ } else {
+ megaCfg->nWrites[(int)ldrv_num]++;
+ megaCfg->nWriteBlocks[(int)ldrv_num] +=
+ mbox->numsectors;
+ }
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10
+ || *SCpnt->cmnd == READ_12) {
+ pScb->dma_direction = PCI_DMA_FROMDEVICE;
+ } else { /*WRITE_6 or WRITE_10 */
+ pScb->dma_direction = PCI_DMA_TODEVICE;
+ }
+#endif
+
+ /* Calculate Scatter-Gather info */
+ mbox->numsgelements = mega_build_sglist (megaCfg, pScb,
+ (u32 *)&mbox->xferaddr, (u32 *)&seg);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pScb->iDataSize = seg;
+
+ if (mbox->numsgelements) {
+ pScb->dma_type = M_RD_SGLIST_ONLY;
+ TRACE1 (("M_RD_SGLIST_ONLY Enabled \n"));
+ } else {
+ pScb->dma_type = M_RD_BULK_DATA_ONLY;
+ TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n"));
+ }
+#endif
+
+ return pScb;
+ default:
+ SCpnt->result = (DID_BAD_TARGET << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+ }
+ /*-----------------------------------------------------
+ *
+ * Passthru drive commands
+ *
+ *-----------------------------------------------------*/
+ else {
+ /* Allocate a SCB and initialize passthru */
+ if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) {
+ SCpnt->result = (DID_ERROR << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+
+ mbox = (mega_mailbox *) pScb->mboxData;
+ memset (mbox, 0, sizeof (pScb->mboxData));
+
+ if ( megaCfg->support_ext_cdb && SCpnt->cmd_len > 10 ) {
+ epthru = mega_prepare_extpassthru(megaCfg, pScb, SCpnt, channel,
+ target);
+ mbox->cmd = MEGA_MBOXCMD_EXTPASSTHRU;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ mbox->xferaddr = pScb->dma_ext_passthruhandle64;
+
+ if(epthru->numsgelements) {
+ pScb->dma_type = M_RD_PTHRU_WITH_SGLIST;
+ } else {
+ pScb->dma_type = M_RD_EPTHRU_WITH_BULK_DATA;
+ }
+#else
+ mbox->xferaddr = virt_to_bus(epthru);
+#endif
+ }
+ else {
+ pthru = mega_prepare_passthru(megaCfg, pScb, SCpnt, channel,
+ target);
+
+ /* Initialize mailbox */
+ mbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ mbox->xferaddr = pScb->dma_passthruhandle64;
+
+ if (pthru->numsgelements) {
+ pScb->dma_type = M_RD_PTHRU_WITH_SGLIST;
+ } else {
+ pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA;
+ }
+#else
+ mbox->xferaddr = virt_to_bus(pthru);
+#endif
+ }
+ return pScb;
+ }
+ return NULL;
+}
+
+static int
+mega_get_ldrv_num(mega_host_config *this_hba, Scsi_Cmnd *sc, int channel)
+{
+ int tgt;
+ int ldrv_num;
+
+ tgt = sc->target;
+
+ if ( tgt > 7 ) tgt--; /* we do not get inquires for tgt 7 */
+
+ ldrv_num = (channel * 15) + tgt; /* 14 targets per channel */
+
+ /*
+ * If we have a logical drive with boot enabled, project it first
+ */
+ if( this_hba->boot_ldrv_enabled ) {
+ if( ldrv_num == 0 ) {
+ ldrv_num = this_hba->boot_ldrv;
+ }
+ else {
+ if( ldrv_num <= this_hba->boot_ldrv ) {
+ ldrv_num--;
+ }
+ }
+ }
+
+ /*
+ * If "delete logical drive" feature is enabled on this controller.
+ * Do only if at least one delete logical drive operation was done.
+ *
+ * Also, after logical drive deletion, instead of logical drive number,
+ * the value returned should be 0x80+logical drive id.
+ *
+ * These is valid only for IO commands.
+ */
+
+ if( this_hba->support_random_del && this_hba->read_ldidmap ) {
+ switch(sc->cmnd[0]) {
+ case READ_6: /* fall through */
+ case WRITE_6: /* fall through */
+ case READ_10: /* fall through */
+ case WRITE_10:
+ ldrv_num += 0x80;
+ }
+ }
+
+ return ldrv_num;
+}
+
+
+static mega_passthru *
+mega_prepare_passthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc,
+ int channel, int target)
+{
+ mega_passthru *pthru;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pthru = scb->pthru;
+#else
+ pthru = &scb->pthru;
+#endif
+ memset (pthru, 0, sizeof (mega_passthru));
+
+ /* set adapter timeout value to 10 min. for tape drive */
+ /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ pthru->timeout = 2;
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 0;
+ pthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : channel;
+ pthru->target = (megacfg->flag & BOARD_40LD) ?
+ (channel << 4) | target : target;
+ pthru->cdblen = sc->cmd_len;
+ pthru->logdrv = sc->lun;
+
+ memcpy (pthru->cdb, sc->cmnd, sc->cmd_len);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* Not sure about the direction */
+ scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+
+ /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */
+ switch (sc->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+
+ if(!(megacfg->flag & (1L << sc->channel))) {
+ printk(KERN_NOTICE
+ "scsi%d: scanning physical channel %d for devices.\n",
+ megacfg->host->host_no, channel);
+
+ megacfg->flag |= (1L << sc->channel);
+ }
+
+ pthru->numsgelements = 0;
+ pthru->dataxferaddr = scb->dma_bounce_buffer;
+ pthru->dataxferlen = sc->request_bufflen;
+ break;
+ default:
+ pthru->numsgelements =
+ mega_build_sglist(
+ megacfg, scb, (u32 *)&pthru->dataxferaddr,
+ (u32 *)&pthru->dataxferlen
+ );
+ break;
+ }
+#else
+ pthru->numsgelements =
+ mega_build_sglist(
+ megacfg, scb, (u32 *)&pthru->dataxferaddr,
+ (u32 *)&pthru->dataxferlen
+ );
+#endif
+ return pthru;
+}
+
+static mega_ext_passthru *
+mega_prepare_extpassthru(mega_host_config *megacfg, mega_scb *scb,
+ Scsi_Cmnd *sc, int channel, int target)
+{
+ mega_ext_passthru *epthru;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ epthru = scb->epthru;
+#else
+ epthru = &scb->epthru;
+#endif
+ memset(epthru, 0, sizeof(mega_ext_passthru));
+
+ /* set adapter timeout value to 10 min. for tape drive */
+ /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ epthru->timeout = 2;
+ epthru->ars = 1;
+ epthru->reqsenselen = 14;
+ epthru->islogical = 0;
+ epthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : channel;
+ epthru->target = (megacfg->flag & BOARD_40LD) ?
+ (channel << 4) | target : target;
+ epthru->cdblen = sc->cmd_len;
+ epthru->logdrv = sc->lun;
+
+ memcpy(epthru->cdb, sc->cmnd, sc->cmd_len);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* Not sure about the direction */
+ scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+
+ /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */
+ switch (sc->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+ if(!(megacfg->flag & (1L << sc->channel))) {
+ printk(KERN_NOTICE
+ "scsi%d: scanning physical channel %d for devices.\n",
+ megacfg->host->host_no, channel);
+
+ megacfg->flag |= (1L << sc->channel);
+ }
+
+ epthru->numsgelements = 0;
+ epthru->dataxferaddr = scb->dma_bounce_buffer;
+ epthru->dataxferlen = sc->request_bufflen;
+ break;
+ default:
+ epthru->numsgelements =
+ mega_build_sglist(
+ megacfg, scb, (u32 *)&epthru->dataxferaddr,
+ (u32 *)&epthru->dataxferlen
+ );
+ break;
+ }
+#else
+ epthru->numsgelements =
+ mega_build_sglist(
+ megacfg, scb, (u32 *)&epthru->dataxferaddr,
+ (u32 *)&epthru->dataxferlen
+ );
+#endif
+ return epthru;
+}
+
+/* Handle Driver Level IOCTLs
+ * Return value of 0 indicates this function could not handle , so continue
+ * processing
+*/
+
+static int mega_driver_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
+{
+ unsigned char *data = (unsigned char *) SCpnt->request_buffer;
+ mega_driver_info driver_info;
+
+ /* If this is not our command dont do anything */
+ if (SCpnt->cmnd[0] != M_RD_DRIVER_IOCTL_INTERFACE)
+ return 0;
+
+ switch (SCpnt->cmnd[1]) {
+ case GET_DRIVER_INFO:
+ if (SCpnt->request_bufflen < sizeof (driver_info)) {
+ SCpnt->result = DID_BAD_TARGET << 16;
+ callDone (SCpnt);
+ return 1;
+ }
+
+ driver_info.size = sizeof (driver_info) - sizeof (int);
+ driver_info.version = MEGARAID_IOCTL_VERSION;
+ memcpy (data, &driver_info, sizeof (driver_info));
+ break;
+ default:
+ SCpnt->result = DID_BAD_TARGET << 16;
+ }
+
+ callDone (SCpnt);
+ return 1;
+}
+
+static void inline set_mbox_xfer_addr (mega_host_config * megaCfg, mega_scb * pScb,
+ mega_ioctl_mbox * mbox, u32 direction)
+{
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ switch (direction) {
+ case TO_DEVICE:
+ pScb->dma_direction = PCI_DMA_TODEVICE;
+ break;
+ case FROM_DEVICE:
+ pScb->dma_direction = PCI_DMA_FROMDEVICE;
+ break;
+ case FROMTO_DEVICE:
+ pScb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+ break;
+ }
+
+ pScb->dma_h_bulkdata
+ = pci_map_single (megaCfg->dev,
+ pScb->buff_ptr,
+ pScb->iDataSize, pScb->dma_direction);
+ mbox->xferaddr = pScb->dma_h_bulkdata;
+ pScb->dma_type = M_RD_BULK_DATA_ONLY;
+ TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n"));
+#else
+ mbox->xferaddr = virt_to_bus (pScb->buff_ptr);
+#endif
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+
+/*--------------------------------------------------------------------
+ * build RAID commands for controller, passed down through ioctl()
+ *--------------------------------------------------------------------*/
+static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
+{
+ mega_scb *pScb;
+ mega_ioctl_mbox *mbox;
+ mega_mailbox *mailbox;
+ mega_passthru *pthru;
+ u8 *mboxdata;
+ long seg, i = 0;
+ unsigned char *data = (unsigned char *) SCpnt->request_buffer;
+
+ if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) {
+ SCpnt->result = (DID_ERROR << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+ pthru = &pScb->pthru;
+
+ mboxdata = (u8 *) & pScb->mboxData;
+ mbox = (mega_ioctl_mbox *) & pScb->mboxData;
+ mailbox = (mega_mailbox *) & pScb->mboxData;
+ memset (mailbox, 0, sizeof (pScb->mboxData));
+
+ if (data[0] == 0x03) { /* passthrough command */
+ unsigned char cdblen = data[2];
+ memset (pthru, 0, sizeof (mega_passthru));
+ pthru->islogical = (data[cdblen + 3] & 0x80) ? 1 : 0;
+ pthru->timeout = data[cdblen + 3] & 0x07;
+ pthru->reqsenselen = 14;
+ pthru->ars = (data[cdblen + 3] & 0x08) ? 1 : 0;
+ pthru->logdrv = data[cdblen + 4];
+ pthru->channel = data[cdblen + 5];
+ pthru->target = data[cdblen + 6];
+ pthru->cdblen = cdblen;
+ memcpy (pthru->cdb, &data[3], cdblen);
+
+ mailbox->cmd = MEGA_MBOXCMD_PASSTHRU;
+
+
+ pthru->numsgelements = mega_build_sglist (megaCfg, pScb,
+ (u32 *) & pthru->
+ dataxferaddr,
+ (u32 *) & pthru->
+ dataxferlen);
+
+ mailbox->xferaddr = virt_to_bus (pthru);
+
+ for (i = 0; i < (SCpnt->request_bufflen - cdblen - 7); i++) {
+ data[i] = data[i + cdblen + 7];
+ }
+ return pScb;
+ }
+ /* else normal (nonpassthru) command */
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /*0x020024 */
+ /*
+ *usage of the function copy from user is used in case of data more than
+ *4KB.This is used only with adapters which supports more than 8 logical
+ * drives.This feature is disabled on kernels earlier or same as 2.0.36
+ * as the uaccess.h file is not available with those kernels.
+ */
+
+ if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) {
+ /* use external data area for large xfers */
+ /* If cmnd[0] is set to M_RD_IOCTL_CMD_NEW then *
+ * cmnd[4..7] = external user buffer *
+ * cmnd[8..11] = length of buffer *
+ * */
+ char *user_area = (char *)*((u32*)&SCpnt->cmnd[4]);
+ u32 xfer_size = *((u32 *) & SCpnt->cmnd[8]);
+ switch (data[0]) {
+ case FW_FIRE_WRITE:
+ case FW_FIRE_FLASH:
+ if ((ulong) user_area & (PAGE_SIZE - 1)) {
+ printk
+ ("megaraid:user address not aligned on 4K boundary.Error.\n");
+ SCpnt->result = (DID_ERROR << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!(pScb->buff_ptr = kmalloc (xfer_size, GFP_KERNEL))) {
+ printk
+ ("megaraid: Insufficient mem for M_RD_IOCTL_CMD_NEW.\n");
+ SCpnt->result = (DID_ERROR << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+
+ copy_from_user (pScb->buff_ptr, user_area, xfer_size);
+ pScb->iDataSize = xfer_size;
+
+ switch (data[0]) {
+ case DCMD_FC_CMD:
+ switch (data[1]) {
+ case DCMD_FC_READ_NVRAM_CONFIG:
+ case DCMD_GET_DISK_CONFIG:
+ {
+ if ((ulong) pScb->
+ buff_ptr & (PAGE_SIZE - 1)) {
+ printk
+ ("megaraid:user address not sufficient Error.\n");
+ SCpnt->result =
+ (DID_ERROR << 16);
+ callDone (SCpnt);
+ return NULL;
+ }
+
+ /*building SG list */
+ mega_build_kernel_sg (pScb->buff_ptr,
+ xfer_size,
+ pScb, mbox);
+ break;
+ }
+ default:
+ break;
+ } /*switch (data[1]) */
+ break;
+ }
+
+ }
+#endif
+
+ mbox->cmd = data[0];
+ mbox->channel = data[1];
+ mbox->param = data[2];
+ mbox->pad[0] = data[3];
+ mbox->logdrv = data[4];
+
+ if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) {
+ switch (data[0]) {
+ case FW_FIRE_WRITE:
+ mbox->cmd = FW_FIRE_WRITE;
+ mbox->channel = data[1]; /* Current Block Number */
+ set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE);
+ mbox->numsgelements = 0;
+ break;
+ case FW_FIRE_FLASH:
+ mbox->cmd = FW_FIRE_FLASH;
+ mbox->channel = data[1] | 0x80; /* Origin */
+ set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE);
+ mbox->numsgelements = 0;
+ break;
+ case DCMD_FC_CMD:
+ *(mboxdata + 0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD */
+ *(mboxdata + 2) = data[1]; /*sub command */
+ switch (data[1]) {
+ case DCMD_FC_READ_NVRAM_CONFIG:
+ case DCMD_FC_READ_NVRAM_CONFIG_64:
+ /* number of elements in SG list */
+ *(mboxdata + 3) = mbox->numsgelements;
+ if (megaCfg->flag & BOARD_64BIT)
+ *(mboxdata + 2) =
+ DCMD_FC_READ_NVRAM_CONFIG_64;
+ break;
+ case DCMD_WRITE_CONFIG:
+ case DCMD_WRITE_CONFIG_64:
+ if (megaCfg->flag & BOARD_64BIT)
+ *(mboxdata + 2) = DCMD_WRITE_CONFIG_64;
+ set_mbox_xfer_addr (megaCfg, pScb, mbox,
+ TO_DEVICE);
+ mbox->numsgelements = 0;
+ break;
+ case DCMD_GET_DISK_CONFIG:
+ case DCMD_GET_DISK_CONFIG_64:
+ if (megaCfg->flag & BOARD_64BIT)
+ *(mboxdata + 2) =
+ DCMD_GET_DISK_CONFIG_64;
+ *(mboxdata + 3) = data[2]; /*number of elements in SG list */
+ /*nr of elements in SG list */
+ *(mboxdata + 4) = mbox->numsgelements;
+ break;
+ case DCMD_DELETE_LOGDRV:
+ case DCMD_DELETE_DRIVEGROUP:
+ case NC_SUBOP_ENQUIRY3:
+ *(mboxdata + 3) = data[2];
+ set_mbox_xfer_addr (megaCfg, pScb, mbox,
+ FROMTO_DEVICE);
+ mbox->numsgelements = 0;
+ break;
+ case DCMD_CHANGE_LDNO:
+ case DCMD_CHANGE_LOOPID:
+ *(mboxdata + 3) = data[2];
+ *(mboxdata + 4) = data[3];
+ set_mbox_xfer_addr (megaCfg, pScb, mbox,
+ TO_DEVICE);
+ mbox->numsgelements = 0;
+ break;
+ default:
+ set_mbox_xfer_addr (megaCfg, pScb, mbox,
+ FROMTO_DEVICE);
+ mbox->numsgelements = 0;
+ break;
+ } /*switch */
+ break;
+ default:
+ set_mbox_xfer_addr (megaCfg, pScb, mbox, FROMTO_DEVICE);
+ mbox->numsgelements = 0;
+ break;
+ }
+ } else {
+
+ mbox->numsgelements = mega_build_sglist (megaCfg, pScb,
+ (u32 *) & mbox->
+ xferaddr,
+ (u32 *) & seg);
+
+ /* Handling some of the fw special commands */
+ switch (data[0]) {
+ case 6: /* START_DEV */
+ mbox->xferaddr = *((u32 *) & data[i + 6]);
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < (SCpnt->request_bufflen - 6); i++) {
+ data[i] = data[i + 6];
+ }
+ }
+
+ return (pScb);
+}
+
+
+static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, mega_ioctl_mbox * mbox)
+{
+ ulong i, buffer_area, len, end, end_page, x, idx = 0;
+
+ buffer_area = (ulong) barea;
+ i = buffer_area;
+ end = buffer_area + xfersize;
+ end_page = (end) & ~(PAGE_SIZE - 1);
+
+ do {
+ len = PAGE_SIZE - (i % PAGE_SIZE);
+ x = pScb->sgList[idx].address =
+ virt_to_bus ((volatile void *) i);
+ pScb->sgList[idx].length = len;
+ i += len;
+ idx++;
+ } while (i < end_page);
+
+ if ((end - i) < 0) {
+ printk ("megaraid:Error in user address\n");
+ }
+
+ if (end - i) {
+ pScb->sgList[idx].address = virt_to_bus ((volatile void *) i);
+ pScb->sgList[idx].length = end - i;
+ idx++;
+ }
+ mbox->xferaddr = virt_to_bus (pScb->sgList);
+ mbox->numsgelements = idx;
+}
+#endif
+
+
+#if DEBUG
+static unsigned int cum_time = 0;
+static unsigned int cum_time_cnt = 0;
+
+static void showMbox (mega_scb * pScb)
+{
+ mega_mailbox *mbox;
+
+ if (pScb == NULL)
+ return;
+
+ mbox = (mega_mailbox *) pScb->mboxData;
+ printk ("%u cmd:%x id:%x #scts:%x lba:%x addr:%x logdrv:%x #sg:%x\n",
+ pScb->SCpnt->pid,
+ mbox->cmd, mbox->cmdid, mbox->numsectors,
+ mbox->lba, mbox->xferaddr, mbox->logdrv, mbox->numsgelements);
+}
+
+#endif
+
+/*--------------------------------------------------------------------
+ * Interrupt service routine
+ *--------------------------------------------------------------------*/
+static void megaraid_isr (int irq, void *devp, struct pt_regs *regs)
+{
+ IO_LOCK_T;
+ mega_host_config * megaCfg;
+ u_char byte, idx, sIdx, tmpBox[MAILBOX_SIZE];
+ u32 dword = 0;
+ mega_mailbox *mbox;
+ mega_scb *pScb;
+ u_char qCnt, qStatus;
+ u_char completed[MAX_FIRMWARE_STATUS];
+ Scsi_Cmnd *SCpnt;
+
+ megaCfg = (mega_host_config *) devp;
+ mbox = (mega_mailbox *) tmpBox;
+
+ if (megaCfg->host->irq == irq) {
+ if (megaCfg->flag & IN_ISR) {
+ TRACE (("ISR called reentrantly!!\n"));
+ printk ("ISR called reentrantly!!\n");
+ }
+ megaCfg->flag |= IN_ISR;
+
+ if (mega_busyWaitMbox (megaCfg)) {
+ printk (KERN_WARNING "Error: mailbox busy in isr!\n");
+ }
+
+ /* Check if a valid interrupt is pending */
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ dword = RDOUTDOOR (megaCfg);
+ if (dword != 0x10001234) {
+ /* Spurious interrupt */
+ megaCfg->flag &= ~IN_ISR;
+ return;
+ }
+ } else {
+ byte = READ_PORT (megaCfg->host->io_port, INTR_PORT);
+ if ((byte & VALID_INTR_BYTE) == 0) {
+ /* Spurious interrupt */
+ megaCfg->flag &= ~IN_ISR;
+ return;
+ }
+ WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte);
+ }
+
+ for (idx = 0; idx < MAX_FIRMWARE_STATUS; idx++)
+ completed[idx] = 0;
+
+ IO_LOCK;
+
+ megaCfg->nInterrupts++;
+ qCnt = 0xff;
+ while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ;
+
+ qStatus = 0xff;
+ while ((qStatus = megaCfg->mbox->status) == 0xFF) ;
+
+ /* Get list of completed requests */
+ for (idx = 0; idx < qCnt; idx++) {
+ while ((sIdx = megaCfg->mbox->completed[idx]) == 0xFF) {
+ printk ("p");
+ }
+ completed[idx] = sIdx;
+ sIdx = 0xFF;
+ }
+
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ WROUTDOOR (megaCfg, dword);
+ /* Acknowledge interrupt */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* In this case mbox contains physical address */
+#if 0
+ WRINDOOR (megaCfg, megaCfg->adjdmahandle64 | 0x2);
+#else
+ WRINDOOR (megaCfg, 0x2);
+#endif
+
+#else
+
+#if 0
+ WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2);
+#else
+ WRINDOOR (megaCfg, 0x2);
+#endif
+
+#endif
+
+#if 0
+ while (RDINDOOR (megaCfg) & 0x02) ;
+#endif
+ } else {
+ CLEAR_INTR (megaCfg->host->io_port);
+ }
+
+#if DEBUG
+ if (qCnt >= MAX_FIRMWARE_STATUS) {
+ printk ("megaraid_isr: cmplt=%d ", qCnt);
+ }
+#endif
+
+ for (idx = 0; idx < qCnt; idx++) {
+ sIdx = completed[idx];
+ if ((sIdx > 0) && (sIdx <= MAX_COMMANDS)) {
+ pScb = &megaCfg->scbList[sIdx - 1];
+
+ /* ASSERT(pScb->state == SCB_ISSUED); */
+
+#if DEBUG
+ if (((jiffies) - pScb->isrcount) > maxCmdTime) {
+ maxCmdTime = (jiffies) - pScb->isrcount;
+ printk
+ ("megaraid_isr : cmd time = %u\n",
+ maxCmdTime);
+ }
+#endif
+ /*
+ * Assuming that the scsi command, for which
+ * an abort request was received earlier, has
+ * completed.
+ */
+ if (pScb->state == SCB_ABORTED) {
+ SCpnt = pScb->SCpnt;
+ }
+ if (pScb->state == SCB_RESET) {
+ SCpnt = pScb->SCpnt;
+ mega_freeSCB (megaCfg, pScb);
+ SCpnt->result = (DID_RESET << 16);
+ if (megaCfg->qCompletedH == NULL) {
+ megaCfg->qCompletedH =
+ megaCfg->qCompletedT =
+ SCpnt;
+ } else {
+ megaCfg->qCompletedT->
+ host_scribble =
+ (unsigned char *) SCpnt;
+ megaCfg->qCompletedT = SCpnt;
+ }
+ megaCfg->qCompletedT->host_scribble =
+ (unsigned char *) NULL;
+ megaCfg->qCcnt++;
+ continue;
+ }
+
+ /* We don't want the ISR routine to touch M_RD_IOCTL_CMD_NEW commands, so
+ * don't mark them as complete, instead we pop their semaphore so
+ * that the queue routine can finish them off
+ */
+ if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) {
+ /* save the status byte for the queue routine to use */
+ pScb->SCpnt->result = qStatus;
+#if XENO_KILLED
+ up (&pScb->ioctl_sem);
+#endif
+ } else {
+ /* Mark command as completed */
+ mega_cmd_done (megaCfg, pScb, qStatus);
+ }
+ } else {
+ printk
+ ("megaraid: wrong cmd id completed from firmware:id=%x\n",
+ sIdx);
+ }
+ }
+
+ mega_rundoneq (megaCfg);
+
+ megaCfg->flag &= ~IN_ISR;
+ /* Loop through any pending requests */
+ mega_runpendq (megaCfg);
+ IO_UNLOCK;
+
+ }
+
+}
+
+/*==================================================*/
+/* Wait until the controller's mailbox is available */
+/*==================================================*/
+
+static int mega_busyWaitMbox (mega_host_config * megaCfg)
+{
+ mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
+ long counter;
+
+ for (counter = 0; counter < 10000; counter++) {
+ if (!mbox->busy) {
+ return 0;
+ }
+ udelay (100);
+ barrier ();
+ }
+ return -1; /* give up after 1 second */
+}
+
+/*=====================================================
+ * Post a command to the card
+ *
+ * Arguments:
+ * mega_host_config *megaCfg - Controller structure
+ * u_char *mboxData - Mailbox area, 16 bytes
+ * mega_scb *pScb - SCB posting (or NULL if N/A)
+ * int intr - if 1, interrupt, 0 is blocking
+ * Return Value: (added on 7/26 for 40ld/64bit)
+ * -1: the command was not actually issued out
+ * other cases:
+ * intr==0, return ScsiStatus, i.e. mbox->status
+ * intr==1, return 0
+ *=====================================================
+ */
+static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData,
+ mega_scb * pScb, int intr)
+{
+ volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ volatile mega_mailbox64 *mbox64 = (mega_mailbox64 *) megaCfg->mbox64;
+#endif
+
+ u_char byte;
+
+#ifdef __LP64__
+ u64 phys_mbox;
+#else
+ u32 phys_mbox;
+#endif
+ u8 retval = -1;
+
+ mboxData[0x1] = (pScb ? pScb->idx + 1 : 0xFE); /* Set cmdid */
+ mboxData[0xF] = 1; /* Set busy */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* In this case mbox contains physical address */
+ phys_mbox = megaCfg->adjdmahandle64;
+#else
+ phys_mbox = virt_to_bus (megaCfg->mbox);
+#endif
+
+#if DEBUG
+ ShowMbox (pScb);
+#endif
+
+ /* Wait until mailbox is free */
+ if (mega_busyWaitMbox (megaCfg)) {
+ printk ("Blocked mailbox......!!\n");
+ udelay (1000);
+
+#if DEBUG
+ showMbox (pLastScb);
+#endif
+
+ /* Abort command */
+ if (pScb == NULL) {
+ TRACE (("NULL pScb in megaIssue\n"));
+ printk ("NULL pScb in megaIssue\n");
+ }
+ mega_cmd_done (megaCfg, pScb, 0x08);
+ return -1;
+ }
+
+ pLastScb = pScb;
+
+ /* Copy mailbox data into host structure */
+ megaCfg->mbox64->xferSegment_lo = 0;
+ megaCfg->mbox64->xferSegment_hi = 0;
+
+ memcpy ((char *) mbox, mboxData, 16);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ switch (mboxData[0]) {
+ case MEGA_MBOXCMD_LREAD64:
+ case MEGA_MBOXCMD_LWRITE64:
+ mbox64->xferSegment_lo = mbox->xferaddr;
+ mbox64->xferSegment_hi = 0;
+ mbox->xferaddr = 0xFFFFFFFF;
+ break;
+ }
+#endif
+
+ /* Kick IO */
+ if (intr) {
+ /* Issue interrupt (non-blocking) command */
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ mbox->mraid_poll = 0;
+ mbox->mraid_ack = 0;
+
+ WRINDOOR (megaCfg, phys_mbox | 0x1);
+ } else {
+ ENABLE_INTR (megaCfg->host->io_port);
+ ISSUE_COMMAND (megaCfg->host->io_port);
+ }
+ pScb->state = SCB_ISSUED;
+
+ retval = 0;
+ } else { /* Issue non-ISR (blocking) command */
+ disable_irq (megaCfg->host->irq);
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ mbox->mraid_poll = 0;
+ mbox->mraid_ack = 0;
+ mbox->numstatus = 0xFF;
+ mbox->status = 0xFF;
+ WRINDOOR (megaCfg, phys_mbox | 0x1);
+
+ while (mbox->numstatus == 0xFF) ;
+ while (mbox->status == 0xFF) ;
+ while (mbox->mraid_poll != 0x77) ;
+ mbox->mraid_poll = 0;
+ mbox->mraid_ack = 0x77;
+
+ /* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234);
+ WROUTDOOR (megaCfg, cmdDone); */
+
+ if (pScb) {
+ mega_cmd_done (megaCfg, pScb, mbox->status);
+ }
+
+ WRINDOOR (megaCfg, phys_mbox | 0x2);
+ while (RDINDOOR (megaCfg) & 0x2) ;
+
+ } else {
+ DISABLE_INTR (megaCfg->host->io_port);
+ ISSUE_COMMAND (megaCfg->host->io_port);
+
+ while (!
+ ((byte =
+ READ_PORT (megaCfg->host->io_port,
+ INTR_PORT)) & INTR_VALID)) ;
+ WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte);
+
+ ENABLE_INTR (megaCfg->host->io_port);
+ CLEAR_INTR (megaCfg->host->io_port);
+
+ if (pScb) {
+ mega_cmd_done (megaCfg, pScb, mbox->status);
+ } else {
+ TRACE (("Error: NULL pScb!\n"));
+ }
+ }
+ enable_irq (megaCfg->host->irq);
+ retval = mbox->status;
+ }
+#if DEBUG
+ while (mega_busyWaitMbox (megaCfg)) {
+ printk(KERN_ERR "Blocked mailbox on exit......!\n");
+ udelay (1000);
+ }
+#endif
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------
+ * Copies data to SGLIST
+ *-------------------------------------------------------------------*/
+/* Note:
+ For 64 bit cards, we need a minimum of one SG element for read/write
+*/
+
+static int
+mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb,
+ u32 * buffer, u32 * length)
+{
+ struct scatterlist *sgList;
+ int idx;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ int sgcnt;
+#endif
+
+ mega_mailbox *mbox = NULL;
+
+ mbox = (mega_mailbox *) scb->mboxData;
+ /* Scatter-gather not used */
+ if (scb->SCpnt->use_sg == 0) {
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ scb->dma_h_bulkdata = pci_map_single (megaCfg->dev,
+ scb->SCpnt->request_buffer,
+ scb->SCpnt->request_bufflen,
+ scb->dma_direction);
+ /* We need to handle special commands like READ64, WRITE64
+ as they need a minimum of 1 SG irrespective of actually SG
+ */
+ if ((megaCfg->flag & BOARD_64BIT) &&
+ ((mbox->cmd == MEGA_MBOXCMD_LREAD64) ||
+ (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) {
+ scb->sg64List[0].address = scb->dma_h_bulkdata;
+ scb->sg64List[0].length = scb->SCpnt->request_bufflen;
+ *buffer = scb->dma_sghandle64;
+ *length = (u32)scb->SCpnt->request_bufflen;
+ scb->sglist_count = 1;
+ return 1;
+ } else {
+ *buffer = scb->dma_h_bulkdata;
+ *length = (u32) scb->SCpnt->request_bufflen;
+ }
+#else
+ *buffer = virt_to_bus (scb->SCpnt->request_buffer);
+ *length = (u32) scb->SCpnt->request_bufflen;
+#endif
+ return 0;
+ }
+
+ sgList = (struct scatterlist *) scb->SCpnt->request_buffer;
+#if 0
+ if (scb->SCpnt->use_sg == 1) {
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ scb->dma_h_bulkdata = pci_map_single (megaCfg->dev,
+ sgList[0].address,
+ sgList[0].length, scb->dma_direction);
+
+ if ((megaCfg->flag & BOARD_64BIT) &&
+ ((mbox->cmd == MEGA_MBOXCMD_LREAD64) ||
+ (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) {
+ scb->sg64List[0].address = scb->dma_h_bulkdata;
+ scb->sg64List[0].length = scb->SCpnt->request_bufflen;
+ *buffer = scb->dma_sghandle64;
+ *length = 0;
+ scb->sglist_count = 1;
+ return 1;
+ } else {
+ *buffer = scb->dma_h_bulkdata;
+ *length = (u32) sgList[0].length;
+ }
+#else
+ *buffer = virt_to_bus (sgList[0].address);
+ *length = (u32) sgList[0].length;
+#endif
+
+ return 0;
+ }
+#endif
+ /* Copy Scatter-Gather list info into controller structure */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ sgcnt = pci_map_sg (megaCfg->dev,
+ sgList, scb->SCpnt->use_sg, scb->dma_direction);
+
+ /* Determine the validity of the new count */
+ if (sgcnt == 0)
+ printk ("pci_map_sg returned zero!!! ");
+
+ for (idx = 0; idx < sgcnt; idx++, sgList++) {
+
+ if ((megaCfg->flag & BOARD_64BIT) &&
+ ((mbox->cmd == MEGA_MBOXCMD_LREAD64) ||
+ (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) {
+ scb->sg64List[idx].address = sg_dma_address (sgList);
+ scb->sg64List[idx].length = sg_dma_len (sgList);
+ } else {
+ scb->sgList[idx].address = sg_dma_address (sgList);
+ scb->sgList[idx].length = sg_dma_len (sgList);
+ }
+
+ }
+
+#else
+ for (idx = 0; idx < scb->SCpnt->use_sg; idx++) {
+ scb->sgList[idx].address = virt_to_bus (sgList[idx].address);
+ scb->sgList[idx].length = (u32) sgList[idx].length;
+ }
+#endif
+
+ /* Reset pointer and length fields */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ *buffer = scb->dma_sghandle64;
+ scb->sglist_count = scb->SCpnt->use_sg;
+#else
+ *buffer = virt_to_bus (scb->sgList);
+#endif
+
+#if 0
+ *length = 0;
+#endif
+ /*
+ * For passthru command, dataxferlen must be set, even for commands with a
+ * sg list
+ */
+ *length = (u32)scb->SCpnt->request_bufflen;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /* Return count of SG requests */
+ return sgcnt;
+#else
+ /* Return count of SG requests */
+ return scb->SCpnt->use_sg;
+#endif
+}
+
+/*--------------------------------------------------------------------
+ * Initializes the address of the controller's mailbox register
+ * The mailbox register is used to issue commands to the card.
+ * Format of the mailbox area:
+ * 00 01 command
+ * 01 01 command id
+ * 02 02 # of sectors
+ * 04 04 logical bus address
+ * 08 04 physical buffer address
+ * 0C 01 logical drive #
+ * 0D 01 length of scatter/gather list
+ * 0E 01 reserved
+ * 0F 01 mailbox busy
+ * 10 01 numstatus byte
+ * 11 01 status byte
+ *--------------------------------------------------------------------*/
+static int
+mega_register_mailbox (mega_host_config * megaCfg, u32 paddr)
+{
+ /* align on 16-byte boundary */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ megaCfg->mbox = &megaCfg->mailbox64ptr->mailbox;
+#else
+ megaCfg->mbox = &megaCfg->mailbox64.mailbox;
+#endif
+
+#ifdef __LP64__
+ megaCfg->mbox = (mega_mailbox *) ((((u64) megaCfg->mbox) + 16) & ((u64) (-1) ^ 0x0F));
+ megaCfg->adjdmahandle64 = (megaCfg->dma_handle64 + 16) & ((u64) (-1) ^ 0x0F);
+ megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - sizeof (u64));
+ paddr = (paddr + 4 + 16) & ((u64) (-1) ^ 0x0F);
+#else
+ megaCfg->mbox
+ = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xFFFFFFF0);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ megaCfg->adjdmahandle64 = ((megaCfg->dma_handle64 + 16) & 0xFFFFFFF0);
+#endif
+
+ megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - 8);
+ paddr = (paddr + 4 + 16) & 0xFFFFFFF0;
+#endif
+
+ /* Register mailbox area with the firmware */
+ if (!(megaCfg->flag & BOARD_QUARTZ)) {
+ WRITE_PORT (megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF);
+ WRITE_PORT (megaCfg->host->io_port, MBOX_PORT1,
+ (paddr >> 8) & 0xFF);
+ WRITE_PORT (megaCfg->host->io_port, MBOX_PORT2,
+ (paddr >> 16) & 0xFF);
+ WRITE_PORT (megaCfg->host->io_port, MBOX_PORT3,
+ (paddr >> 24) & 0xFF);
+ WRITE_PORT (megaCfg->host->io_port, ENABLE_MBOX_REGION,
+ ENABLE_MBOX_BYTE);
+
+ CLEAR_INTR (megaCfg->host->io_port);
+ ENABLE_INTR (megaCfg->host->io_port);
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * mega_Convert8ldTo40ld() -- takes all info in AdapterInquiry structure and
+ * puts it into ProductInfo and Enquiry3 structures for later use
+ *---------------------------------------------------------------------------*/
+static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry,
+ mega_Enquiry3 * enquiry3,
+ megaRaidProductInfo * productInfo)
+{
+ int i;
+
+ productInfo->MaxConcCmds = inquiry->AdpInfo.MaxConcCmds;
+ enquiry3->rbldRate = inquiry->AdpInfo.RbldRate;
+ productInfo->SCSIChanPresent = inquiry->AdpInfo.ChanPresent;
+
+ for (i = 0; i < 4; i++) {
+ productInfo->FwVer[i] = inquiry->AdpInfo.FwVer[i];
+ productInfo->BiosVer[i] = inquiry->AdpInfo.BiosVer[i];
+ }
+ enquiry3->cacheFlushInterval = inquiry->AdpInfo.CacheFlushInterval;
+ productInfo->DramSize = inquiry->AdpInfo.DramSize;
+
+ enquiry3->numLDrv = inquiry->LogdrvInfo.NumLDrv;
+
+ for (i = 0; i < MAX_LOGICAL_DRIVES; i++) {
+ enquiry3->lDrvSize[i] = inquiry->LogdrvInfo.LDrvSize[i];
+ enquiry3->lDrvProp[i] = inquiry->LogdrvInfo.LDrvProp[i];
+ enquiry3->lDrvState[i]
+ = inquiry->LogdrvInfo.LDrvState[i];
+ }
+
+ for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) {
+ enquiry3->pDrvState[i]
+ = inquiry->PhysdrvInfo.PDrvState[i];
+ }
+}
+
+/*-------------------------------------------------------------------
+ * Issue an adapter info query to the controller
+ *-------------------------------------------------------------------*/
+static int mega_i_query_adapter (mega_host_config * megaCfg)
+{
+ mega_Enquiry3 *enquiry3Pnt;
+ mega_mailbox *mbox;
+ u_char mboxData[16];
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ dma_addr_t raid_inq_dma_handle = 0, prod_info_dma_handle = 0, enquiry3_dma_handle = 0;
+#endif
+ u8 retval;
+
+ /* Initialize adapter inquiry mailbox */
+
+ mbox = (mega_mailbox *) mboxData;
+
+ memset ((void *) megaCfg->mega_buffer, 0,
+ sizeof (megaCfg->mega_buffer));
+ memset (mbox, 0, 16);
+
+/*
+ * Try to issue Enquiry3 command
+ * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and
+ * update enquiry3 structure
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ enquiry3_dma_handle = pci_map_single (megaCfg->dev,
+ (void *) megaCfg->mega_buffer,
+ (2 * 1024L), PCI_DMA_FROMDEVICE);
+
+ mbox->xferaddr = enquiry3_dma_handle;
+#else
+ /*Taken care */
+ mbox->xferaddr = virt_to_bus ((void *) megaCfg->mega_buffer);
+#endif
+
+ /* Initialize mailbox databuffer addr */
+ enquiry3Pnt = (mega_Enquiry3 *) megaCfg->mega_buffer;
+ /* point mega_Enguiry3 to the data buf */
+
+ mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
+ mboxData[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */
+ mboxData[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */
+
+ /* Issue a blocking command to the card */
+ if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) { /* the adapter does not support 40ld */
+ mega_RAIDINQ adapterInquiryData;
+ mega_RAIDINQ *adapterInquiryPnt = &adapterInquiryData;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ raid_inq_dma_handle = pci_map_single (megaCfg->dev,
+ (void *) adapterInquiryPnt,
+ sizeof (mega_RAIDINQ),
+ PCI_DMA_FROMDEVICE);
+ mbox->xferaddr = raid_inq_dma_handle;
+#else
+ /*taken care */
+ mbox->xferaddr = virt_to_bus ((void *) adapterInquiryPnt);
+#endif
+
+ mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ; /*issue old 0x05 command to adapter */
+ /* Issue a blocking command to the card */ ;
+ retval = megaIssueCmd (megaCfg, mboxData, NULL, 0);
+
+ pci_unmap_single (megaCfg->dev,
+ raid_inq_dma_handle,
+ sizeof (mega_RAIDINQ), PCI_DMA_FROMDEVICE);
+
+ /*update Enquiry3 and ProductInfo structures with mega_RAIDINQ structure*/
+ mega_Convert8ldTo40ld (adapterInquiryPnt,
+ enquiry3Pnt,
+ (megaRaidProductInfo *) & megaCfg->
+ productInfo);
+
+ } else { /* adapter supports 40ld */
+ megaCfg->flag |= BOARD_40LD;
+
+ pci_unmap_single (megaCfg->dev,
+ enquiry3_dma_handle,
+ (2 * 1024L), PCI_DMA_FROMDEVICE);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+/*get productInfo, which is static information and will be unchanged*/
+ prod_info_dma_handle
+ = pci_map_single (megaCfg->dev,
+ (void *) &megaCfg->productInfo,
+ sizeof (megaRaidProductInfo),
+ PCI_DMA_FROMDEVICE);
+ mbox->xferaddr = prod_info_dma_handle;
+#else
+ /*taken care */
+ mbox->xferaddr = virt_to_bus ((void *) &megaCfg->productInfo);
+#endif
+
+ mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
+ mboxData[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */
+
+ if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0)
+ printk ("megaraid: Product_info cmd failed with error: %d\n",
+ retval);
+
+ pci_unmap_single (megaCfg->dev,
+ prod_info_dma_handle,
+ sizeof (megaRaidProductInfo),
+ PCI_DMA_FROMDEVICE);
+ }
+
+ /*
+ * kernel scans the channels from 0 to <= max_channel
+ */
+ megaCfg->host->max_channel =
+ megaCfg->productInfo.SCSIChanPresent + NVIRT_CHAN -1;
+
+ megaCfg->host->max_id = 16; /* max targets per channel */
+
+ megaCfg->host->max_lun = 7; /* Upto 7 luns for non disk devices */
+
+ megaCfg->host->cmd_per_lun = MAX_CMD_PER_LUN;
+
+ megaCfg->numldrv = enquiry3Pnt->numLDrv;
+ megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds;
+ if (megaCfg->max_cmds > MAX_COMMANDS)
+ megaCfg->max_cmds = MAX_COMMANDS - 1;
+
+ megaCfg->host->can_queue = megaCfg->max_cmds - 1;
+
+ /* use HP firmware and bios version encoding */
+ if (megaCfg->productInfo.subSystemVendorID == HP_SUBSYS_ID) {
+ sprintf (megaCfg->fwVer, "%c%d%d.%d%d",
+ megaCfg->productInfo.FwVer[2],
+ megaCfg->productInfo.FwVer[1] >> 8,
+ megaCfg->productInfo.FwVer[1] & 0x0f,
+ megaCfg->productInfo.FwVer[0] >> 8,
+ megaCfg->productInfo.FwVer[0] & 0x0f);
+ sprintf (megaCfg->biosVer, "%c%d%d.%d%d",
+ megaCfg->productInfo.BiosVer[2],
+ megaCfg->productInfo.BiosVer[1] >> 8,
+ megaCfg->productInfo.BiosVer[1] & 0x0f,
+ megaCfg->productInfo.BiosVer[0] >> 8,
+ megaCfg->productInfo.BiosVer[0] & 0x0f);
+ } else {
+ memcpy (megaCfg->fwVer, (char *) megaCfg->productInfo.FwVer, 4);
+ megaCfg->fwVer[4] = 0;
+
+ memcpy (megaCfg->biosVer, (char *) megaCfg->productInfo.BiosVer, 4);
+ megaCfg->biosVer[4] = 0;
+ }
+ megaCfg->support_ext_cdb = mega_support_ext_cdb(megaCfg);
+
+ printk (KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR,
+ megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv);
+
+ if ( megaCfg->support_ext_cdb ) {
+ printk(KERN_NOTICE "megaraid: supports extended CDBs.\n");
+ }
+
+ /*
+ * I hope that I can unmap here, reason DMA transaction is not required any more
+ * after this
+ */
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Driver interface functions
+ *
+ *-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------
+ * Returns data to be displayed in /proc/scsi/megaraid/X
+ *----------------------------------------------------------*/
+#if XENO_KILLED
+int megaraid_proc_info (char *buffer, char **start, off_t offset,
+ int length, int host_no, int inout)
+{
+ *start = buffer;
+ return 0;
+}
+#endif
+
+static int mega_findCard (Scsi_Host_Template * pHostTmpl,
+ u16 pciVendor, u16 pciDev, long flag)
+{
+ mega_host_config *megaCfg = NULL;
+ struct Scsi_Host *host = NULL;
+ u_char pciBus, pciDevFun, megaIrq;
+
+ u16 magic;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ u32 magic64;
+#endif
+
+ int i, j;
+
+#ifdef __LP64__
+ u64 megaBase;
+#else
+ u32 megaBase;
+#endif
+
+ u16 pciIdx = 0;
+ u16 numFound = 0;
+ u16 subsysid, subsysvid;
+#if 0
+ u8 mega_ch_class;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */
+ while (!pcibios_find_device
+ (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) {
+#else
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */
+ struct pci_dev *pdev = NULL;
+#else
+ struct pci_dev *pdev = pci_devices;
+#endif
+
+ while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) {
+ if(pci_enable_device (pdev))
+ continue;
+ pciBus = pdev->bus->number;
+ pciDevFun = pdev->devfn;
+#endif
+ if ((flag & BOARD_QUARTZ) && (skip_id == -1)) {
+ if( (pciVendor == PCI_VENDOR_ID_PERC4_DI_YSTONE &&
+ pciDev == PCI_DEVICE_ID_PERC4_DI_YSTONE) ||
+ (pciVendor == PCI_VENDOR_ID_PERC4_QC_VERDE &&
+ pciDev == PCI_DEVICE_ID_PERC4_QC_VERDE) ) {
+
+ flag |= BOARD_64BIT;
+ }
+ else {
+ pcibios_read_config_word (pciBus, pciDevFun,
+ PCI_CONF_AMISIG, &magic);
+ if ((magic != AMI_SIGNATURE)
+ && (magic != AMI_SIGNATURE_471)) {
+ pciIdx++;
+ continue; /* not an AMI board */
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pcibios_read_config_dword (pciBus, pciDevFun,
+ PCI_CONF_AMISIG64, &magic64);
+
+ if (magic64 == AMI_64BIT_SIGNATURE)
+ flag |= BOARD_64BIT;
+#endif
+ }
+ }
+
+ /* Hmmm...Should we not make this more modularized so that in future we dont add
+ for each firmware */
+
+ if (flag & BOARD_QUARTZ) {
+ /* Check to see if this is a Dell PERC RAID controller model 466 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */
+ pcibios_read_config_word (pciBus, pciDevFun,
+ PCI_SUBSYSTEM_VENDOR_ID,
+ &subsysvid);
+ pcibios_read_config_word (pciBus, pciDevFun,
+ PCI_SUBSYSTEM_ID, &subsysid);
+#else
+ pci_read_config_word (pdev,
+ PCI_SUBSYSTEM_VENDOR_ID,
+ &subsysvid);
+ pci_read_config_word (pdev,
+ PCI_SUBSYSTEM_ID, &subsysid);
+#endif
+
+ /*
+ * If we do not find the valid subsys vendor id, refuse to load
+ * the driver. This is part of PCI200X compliance
+ */
+ if( (subsysvid != AMI_SUBSYS_ID) &&
+ (subsysvid != DELL_SUBSYS_ID) &&
+ (subsysvid != LSI_SUBSYS_ID) &&
+ (subsysvid != HP_SUBSYS_ID) ) continue;
+
+ }
+
+ printk (KERN_NOTICE
+ "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n",
+ pciVendor, pciDev, pciIdx, pciBus, PCI_SLOT (pciDevFun),
+ PCI_FUNC (pciDevFun));
+ /* Read the base port and IRQ from PCI */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */
+ pcibios_read_config_dword (pciBus, pciDevFun,
+ PCI_BASE_ADDRESS_0,
+ (u_int *) & megaBase);
+ pcibios_read_config_byte (pciBus, pciDevFun,
+ PCI_INTERRUPT_LINE, &megaIrq);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */
+ megaBase = pdev->base_address[0];
+ megaIrq = pdev->irq;
+#else
+
+ megaBase = pci_resource_start (pdev, 0);
+ megaIrq = pdev->irq;
+#endif
+
+ pciIdx++;
+
+ if (flag & BOARD_QUARTZ) {
+ megaBase &= PCI_BASE_ADDRESS_MEM_MASK;
+ megaBase = (long) ioremap (megaBase, 128);
+ if (!megaBase)
+ continue;
+ } else {
+ megaBase &= PCI_BASE_ADDRESS_IO_MASK;
+ megaBase += 0x10;
+ }
+
+ /* Initialize SCSI Host structure */
+ host = scsi_register (pHostTmpl, sizeof (mega_host_config));
+ if (!host)
+ goto err_unmap;
+
+ /*
+ * Comment the following initialization if you know 'max_sectors' is
+ * not defined for this kernel.
+ * This field was introduced in Linus's kernel 2.4.7pre3 and it
+ * greatly increases the IO performance - AM
+ */
+ host->max_sectors = 1024;
+
+ scsi_set_pci_device(host, pdev);
+ megaCfg = (mega_host_config *) host->hostdata;
+ memset (megaCfg, 0, sizeof (mega_host_config));
+
+ printk (KERN_NOTICE "scsi%d : Found a MegaRAID controller at 0x%x, IRQ: %d"
+ M_RD_CRLFSTR, host->host_no, (u_int) megaBase, megaIrq);
+
+ if (flag & BOARD_64BIT)
+ printk (KERN_NOTICE "scsi%d : Enabling 64 bit support\n",
+ host->host_no);
+
+ /* Copy resource info into structure */
+ megaCfg->qCompletedH = NULL;
+ megaCfg->qCompletedT = NULL;
+ megaCfg->qPendingH = NULL;
+ megaCfg->qPendingT = NULL;
+ megaCfg->qFreeH = NULL;
+ megaCfg->qFreeT = NULL;
+ megaCfg->qFcnt = 0;
+ megaCfg->qPcnt = 0;
+ megaCfg->qCcnt = 0;
+ megaCfg->lock_free = SPIN_LOCK_UNLOCKED;
+ megaCfg->lock_pend = SPIN_LOCK_UNLOCKED;
+ megaCfg->lock_scsicmd = SPIN_LOCK_UNLOCKED;
+ megaCfg->flag = flag;
+ megaCfg->int_qh = NULL;
+ megaCfg->int_qt = NULL;
+ megaCfg->int_qlen = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ megaCfg->dev = pdev;
+#endif
+ megaCfg->host = host;
+ megaCfg->base = megaBase;
+ megaCfg->host->irq = megaIrq;
+ megaCfg->host->io_port = megaBase;
+ megaCfg->host->n_io_port = 16;
+ megaCfg->host->unique_id = (pciBus << 8) | pciDevFun;
+ megaCtlrs[numCtlrs] = megaCfg;
+
+ if (!(flag & BOARD_QUARTZ)) {
+
+ /* Request our IO Range */
+ if( !request_region(megaBase, 16, "megaraid") )
+ goto err_unregister;
+ }
+
+ /* Request our IRQ */
+ if (request_irq (megaIrq, megaraid_isr, SA_SHIRQ,
+ "megaraid", megaCfg)) {
+ printk (KERN_WARNING
+ "megaraid: Couldn't register IRQ %d!\n",
+ megaIrq);
+ goto err_release;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ /*
+ * unmap while releasing the driver, Is it required to be
+ * PCI_DMA_BIDIRECTIONAL
+ */
+
+ megaCfg->mailbox64ptr
+ = pci_alloc_consistent (megaCfg->dev,
+ sizeof (mega_mailbox64),
+ &(megaCfg->dma_handle64));
+
+ mega_register_mailbox (megaCfg,
+ virt_to_bus ((void *) megaCfg->
+ mailbox64ptr));
+#else
+ mega_register_mailbox (megaCfg,
+ virt_to_bus ((void *) &megaCfg->
+ mailbox64));
+#endif
+
+ mega_i_query_adapter (megaCfg);
+
+ if ((subsysid == 0x1111) && (subsysvid == 0x1111)) {
+
+ /*
+ * Which firmware
+ */
+ if( strcmp(megaCfg->fwVer, "3.00") == 0 ||
+ strcmp(megaCfg->fwVer, "3.01") == 0 ) {
+
+ printk( KERN_WARNING
+ "megaraid: Your card is a Dell PERC 2/SC RAID controller "
+ "with firmware\nmegaraid: 3.00 or 3.01. This driver is "
+ "known to have corruption issues\nmegaraid: with those "
+ "firmware versions on this specific card. In order\n"
+ "megaraid: to protect your data, please upgrade your "
+ "firmware to version\nmegaraid: 3.10 or later, available "
+ "from the Dell Technical Support web\nmegaraid: site at\n"
+ "http://support.dell.com/us/en/filelib/download/"
+ "index.asp?fileid=2940\n"
+ );
+ }
+ }
+
+ /*
+ * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with
+ * firmware H.01.07 or H.01.08, disable 64 bit support,
+ * since this firmware cannot handle 64 bit addressing
+ */
+
+ if( (subsysvid == HP_SUBSYS_ID) &&
+ ((subsysid == 0x60E7)||(subsysid == 0x60E8)) ) {
+
+ /*
+ * which firmware
+ */
+ if( strcmp(megaCfg->fwVer, "H01.07") == 0 ||
+ strcmp(megaCfg->fwVer, "H01.08") == 0 ||
+ strcmp(megaCfg->fwVer, "H01.09") == 0 )
+ {
+ printk(KERN_WARNING
+ "megaraid: Firmware H.01.07/8/9 on 1M/2M "
+ "controllers\nmegaraid: do not support 64 bit "
+ "addressing.\n"
+ "megaraid: DISABLING 64 bit support.\n");
+ megaCfg->flag &= ~BOARD_64BIT;
+ }
+ }
+
+ if (mega_is_bios_enabled (megaCfg)) {
+ mega_hbas[numCtlrs].is_bios_enabled = 1;
+ }
+
+ /*
+ * Find out which channel is raid and which is scsi
+ */
+ mega_enum_raid_scsi(megaCfg);
+
+ /*
+ * Find out if a logical drive is set as the boot drive. If there is
+ * one, will make that as the first logical drive.
+ * ROMB: Do we have to boot from a physical drive. Then all the
+ * physical drives would appear before the logical disks. Else, all
+ * the physical drives would be exported to the mid layer after
+ * logical disks.
+ */
+ mega_get_boot_drv(megaCfg);
+
+ if( ! megaCfg->boot_pdrv_enabled ) {
+ for( i = 0; i < NVIRT_CHAN; i++ )
+ megaCfg->logdrv_chan[i] = 1;
+
+ for( i = NVIRT_CHAN; i < MAX_CHANNEL + NVIRT_CHAN; i++ )
+ megaCfg->logdrv_chan[i] = 0;
+
+ megaCfg->mega_ch_class <<= NVIRT_CHAN;
+ }
+ else {
+ j = megaCfg->productInfo.SCSIChanPresent;
+ for( i = 0; i < j; i++ )
+ megaCfg->logdrv_chan[i] = 0;
+
+ for( i = j; i < NVIRT_CHAN + j; i++ )
+ megaCfg->logdrv_chan[i] = 1;
+ }
+
+
+ mega_hbas[numCtlrs].hostdata_addr = megaCfg;
+
+ /*
+ * Do we support random deletion and addition of logical drives
+ */
+ megaCfg->read_ldidmap = 0; /* set it after first logdrv delete cmd */
+ megaCfg->support_random_del = mega_support_random_del(megaCfg);
+
+ /* Initialize SCBs */
+ if (mega_init_scb (megaCfg)) {
+ pci_free_consistent (megaCfg->dev,
+ sizeof (mega_mailbox64),
+ (void *) megaCfg->mailbox64ptr,
+ megaCfg->dma_handle64);
+ scsi_unregister (host);
+ continue;
+ }
+
+ /*
+ * Fill in the structure which needs to be passed back to the
+ * application when it does an ioctl() for controller related
+ * information.
+ */
+
+ i = numCtlrs;
+ numCtlrs++;
+
+ mcontroller[i].base = megaBase;
+ mcontroller[i].irq = megaIrq;
+ mcontroller[i].numldrv = megaCfg->numldrv;
+ mcontroller[i].pcibus = pciBus;
+ mcontroller[i].pcidev = pciDev;
+ mcontroller[i].pcifun = PCI_FUNC (pciDevFun);
+ mcontroller[i].pciid = pciIdx;
+ mcontroller[i].pcivendor = pciVendor;
+ mcontroller[i].pcislot = PCI_SLOT (pciDevFun);
+ mcontroller[i].uid = (pciBus << 8) | pciDevFun;
+
+ numFound++;
+
+ /* Set the Mode of addressing to 64 bit */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if ((megaCfg->flag & BOARD_64BIT) && BITS_PER_LONG == 64)
+#ifdef __LP64__
+ pdev->dma_mask = 0xffffffffffffffff;
+#else
+ pdev->dma_mask = 0xffffffff;
+#endif
+#endif
+ continue;
+ err_release:
+ if (flag & BOARD_QUARTZ)
+ release_region (megaBase, 16);
+ err_unregister:
+ scsi_unregister (host);
+ err_unmap:
+ if (flag & BOARD_QUARTZ)
+ iounmap ((void *) megaBase);
+ }
+ return numFound;
+}
+
+/*---------------------------------------------------------
+ * Detects if a megaraid controller exists in this system
+ *---------------------------------------------------------*/
+
+int megaraid_detect (Scsi_Host_Template * pHostTmpl)
+{
+#if XENO_KILLED
+ int ctlridx = 0;
+#endif
+ int count = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */
+ pHostTmpl->proc_dir = &proc_scsi_megaraid;
+#else
+ pHostTmpl->proc_name = "megaraid";
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */
+ if (!pcibios_present ()) {
+ printk (KERN_WARNING "megaraid: PCI bios not present."
+ M_RD_CRLFSTR);
+ return 0;
+ }
+#endif
+ skip_id = -1;
+ if (megaraid && !strncmp (megaraid, "skip", strlen ("skip"))) {
+ if (megaraid[4] != '\0') {
+ skip_id = megaraid[4] - '0';
+ if (megaraid[5] != '\0') {
+ skip_id = (skip_id * 10) + (megaraid[5] - '0');
+ }
+ }
+ skip_id = (skip_id > 15) ? -1 : skip_id;
+ }
+
+ printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION);
+
+ memset (mega_hbas, 0, sizeof (mega_hbas));
+
+ /* Detect ROMBs first */
+ count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_DISCOVERY,
+ PCI_DEVICE_ID_DISCOVERY, BOARD_QUARTZ);
+ count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_PERC4_DI_YSTONE,
+ PCI_DEVICE_ID_PERC4_DI_YSTONE, BOARD_QUARTZ);
+ /* Then detect cards based on date they were produced, oldest first */
+ count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID, 0);
+ count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID2, 0);
+ count += mega_findCard (pHostTmpl, 0x8086,
+ PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ);
+ count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI,
+ PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ);
+ count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_LSI_LOGIC,
+ PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ);
+ count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_PERC4_QC_VERDE,
+ PCI_DEVICE_ID_PERC4_QC_VERDE, BOARD_QUARTZ);
+
+ mega_reorder_hosts ();
+
+#ifdef CONFIG_PROC_FS
+ if (count) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */
+ mega_proc_dir_entry = proc_mkdir ("megaraid", &proc_root);
+#else
+ mega_proc_dir_entry = create_proc_entry ("megaraid",
+ S_IFDIR | S_IRUGO |
+ S_IXUGO, &proc_root);
+#endif
+ if (!mega_proc_dir_entry)
+ printk ("megaraid: failed to create megaraid root\n");
+ else
+ for (ctlridx = 0; ctlridx < count; ctlridx++)
+ mega_create_proc_entry (ctlridx,
+ mega_proc_dir_entry);
+ }
+#endif
+
+ /*
+ * Register the driver as a character device, for applications to access
+ * it for ioctls.
+ * Ideally, this should go in the init_module() routine, but since it is
+ * hidden in the file "scsi_module.c" ( included in the end ), we define
+ * it here
+ * First argument (major) to register_chrdev implies a dynamic major
+ * number allocation.
+ */
+#if XENO_KILLED
+ if (count) {
+ major = register_chrdev (0, "megadev", &megadev_fops);
+
+ /*
+ * Register the Shutdown Notification hook in kernel
+ */
+ if (register_reboot_notifier (&mega_notifier)) {
+ printk ("MegaRAID Shutdown routine not registered!!\n");
+ }
+
+ init_MUTEX (&mimd_entry_mtx);
+ }
+#endif
+
+ return count;
+}
+
+/*---------------------------------------------------------------------
+ * Release the controller's resources
+ *---------------------------------------------------------------------*/
+int megaraid_release (struct Scsi_Host *pSHost)
+{
+ mega_host_config *megaCfg;
+ mega_mailbox *mbox;
+ u_char mboxData[16];
+#ifdef CONFIG_PROC_FS
+ int i;
+#endif
+
+ megaCfg = (mega_host_config *) pSHost->hostdata;
+ mbox = (mega_mailbox *) mboxData;
+
+ /* Flush cache to disk */
+ memset (mbox, 0, 16);
+ mboxData[0] = 0xA;
+
+ free_irq (megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise
+ extra interrupt is generated */
+
+ /* Issue a blocking (interrupts disabled) command to the card */
+ megaIssueCmd (megaCfg, mboxData, NULL, 0);
+
+ /* Free our resources */
+ if (megaCfg->flag & BOARD_QUARTZ) {
+ iounmap ((void *) megaCfg->base);
+ } else {
+ release_region (megaCfg->host->io_port, 16);
+ }
+
+ mega_freeSgList (megaCfg);
+ pci_free_consistent (megaCfg->dev,
+ sizeof (mega_mailbox64),
+ (void *) megaCfg->mailbox64ptr,
+ megaCfg->dma_handle64);
+
+#ifdef CONFIG_PROC_FS
+ if (megaCfg->controller_proc_dir_entry) {
+ remove_proc_entry ("stat", megaCfg->controller_proc_dir_entry);
+ remove_proc_entry ("status",
+ megaCfg->controller_proc_dir_entry);
+ remove_proc_entry ("config",
+ megaCfg->controller_proc_dir_entry);
+ remove_proc_entry ("mailbox",
+ megaCfg->controller_proc_dir_entry);
+ for (i = 0; i < numCtlrs; i++) {
+ char buf[12] = { 0 };
+ sprintf (buf, "%d", i);
+ remove_proc_entry (buf, mega_proc_dir_entry);
+ }
+ remove_proc_entry ("megaraid", &proc_root);
+ }
+#endif
+
+ /*
+ * Release the controller memory. A word of warning this frees
+ * hostdata and that includes megaCfg-> so be careful what you
+ * dereference beyond this point
+ */
+
+ scsi_unregister (pSHost);
+
+ /*
+ * Unregister the character device interface to the driver. Ideally this
+ * should have been done in cleanup_module routine. Since this is hidden
+ * in file "scsi_module.c", we do it here.
+ * major is the major number of the character device returned by call to
+ * register_chrdev() routine.
+ */
+
+#if XENO_KILLED
+ unregister_chrdev (major, "megadev");
+ unregister_reboot_notifier (&mega_notifier);
+#endif
+
+ return 0;
+}
+
+static int mega_is_bios_enabled (mega_host_config * megacfg)
+{
+ mega_mailbox *mboxpnt;
+ unsigned char mbox[16];
+ int ret;
+
+ mboxpnt = (mega_mailbox *) mbox;
+
+ memset (mbox, 0, sizeof (mbox));
+ memset ((void *) megacfg->mega_buffer,
+ 0, sizeof (megacfg->mega_buffer));
+
+ /*
+ * issue command to find out if the BIOS is enabled for this controller
+ */
+ mbox[0] = IS_BIOS_ENABLED;
+ mbox[2] = GET_BIOS;
+
+ mboxpnt->xferaddr = virt_to_bus ((void *) megacfg->mega_buffer);
+
+ ret = megaIssueCmd (megacfg, mbox, NULL, 0);
+
+ return (*(char *) megacfg->mega_buffer);
+}
+
+/*
+ * Find out what channels are RAID/SCSI
+ */
+static void
+mega_enum_raid_scsi(mega_host_config *megacfg)
+{
+ mega_mailbox *mboxp;
+ unsigned char mbox[16];
+ int i;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ dma_addr_t dma_handle;
+#endif
+
+ mboxp = (mega_mailbox *)mbox;
+
+ memset(mbox, 0, sizeof(mbox));
+ /*
+ * issue command to find out what channels are raid/scsi
+ */
+ mbox[0] = CHNL_CLASS;
+ mbox[2] = GET_CHNL_CLASS;
+
+ memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer,
+ (2 * 1024L), PCI_DMA_FROMDEVICE);
+
+ mboxp->xferaddr = dma_handle;
+#else
+ mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer);
+#endif
+
+ /*
+ * Non-ROMB firware fail this command, so all channels
+ * must be shown RAID
+ */
+ megacfg->mega_ch_class = 0xFF;
+ if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) {
+ megacfg->mega_ch_class = *((char *)megacfg->mega_buffer);
+ }
+
+ for( i = 0; i < megacfg->productInfo.SCSIChanPresent; i++ ) {
+ if( (megacfg->mega_ch_class >> i) & 0x01 )
+ printk(KERN_NOTICE"megaraid: channel[%d] is raid.\n", i+1);
+ else
+ printk(KERN_NOTICE"megaraid: channel[%d] is scsi.\n", i+1);
+ }
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pci_unmap_single(megacfg->dev, dma_handle,
+ (2 * 1024L), PCI_DMA_FROMDEVICE);
+#endif
+
+}
+
+
+/*
+ * get the boot logical drive number if enabled
+ */
+void
+mega_get_boot_drv(mega_host_config *megacfg)
+{
+ mega_mailbox *mboxp;
+ unsigned char mbox[16];
+ struct private_bios_data *prv_bios_data;
+ u16 cksum = 0;
+ u8 *cksum_p;
+ u8 boot_pdrv;
+ int i;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ dma_addr_t dma_handle;
+#endif
+
+ mboxp = (mega_mailbox *)mbox;
+
+ memset(mbox, 0, sizeof(mbox));
+
+ mbox[0] = BIOS_PVT_DATA;
+ mbox[2] = GET_BIOS_PVT_DATA;
+
+ memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer,
+ (2 * 1024L), PCI_DMA_FROMDEVICE);
+
+ mboxp->xferaddr = dma_handle;
+#else
+ mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer);
+#endif
+
+ megacfg->boot_ldrv_enabled = 0;
+ megacfg->boot_ldrv = 0;
+
+ megacfg->boot_pdrv_enabled = 0;
+ megacfg->boot_pdrv_ch = 0;
+ megacfg->boot_pdrv_tgt = 0;
+
+ if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) {
+ prv_bios_data = (struct private_bios_data *)megacfg->mega_buffer;
+
+ cksum = 0;
+ cksum_p = (u8 *)prv_bios_data;
+ for( i = 0; i < 14; i++ ) {
+ cksum += *cksum_p++;
+ }
+
+ if( prv_bios_data->cksum == (u16)(0-cksum) ) {
+
+ /*
+ * If MSB is set, a physical drive is set as boot device
+ */
+ if( prv_bios_data->boot_drv & 0x80 ) {
+ megacfg->boot_pdrv_enabled = 1;
+ boot_pdrv = prv_bios_data->boot_drv & 0x7F;
+ megacfg->boot_pdrv_ch = boot_pdrv / 16;
+ megacfg->boot_pdrv_tgt = boot_pdrv % 16;
+ }
+ else {
+ megacfg->boot_ldrv_enabled = 1;
+ megacfg->boot_ldrv = prv_bios_data->boot_drv;
+ }
+ }
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pci_unmap_single(megacfg->dev, dma_handle,
+ (2 * 1024L), PCI_DMA_FROMDEVICE);
+#endif
+
+}
+
+
+static void mega_reorder_hosts (void)
+{
+ struct Scsi_Host *shpnt;
+ struct Scsi_Host *shone;
+ struct Scsi_Host *shtwo;
+ mega_host_config *boot_host;
+ int i;
+
+ /*
+ * Find the (first) host which has it's BIOS enabled
+ */
+ boot_host = NULL;
+ for (i = 0; i < MAX_CONTROLLERS; i++) {
+ if (mega_hbas[i].is_bios_enabled) {
+ boot_host = mega_hbas[i].hostdata_addr;
+ break;
+ }
+ }
+
+ if (boot_host == NULL) {
+ printk (KERN_WARNING "megaraid: no BIOS enabled.\n");
+ return;
+ }
+
+ /*
+ * Traverse through the list of SCSI hosts for our HBA locations
+ */
+ shone = shtwo = NULL;
+ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+ /* Is it one of ours? */
+ for (i = 0; i < MAX_CONTROLLERS; i++) {
+ if ((mega_host_config *) shpnt->hostdata ==
+ mega_hbas[i].hostdata_addr) {
+ /* Does this one has BIOS enabled */
+ if (mega_hbas[i].hostdata_addr == boot_host) {
+
+ /* Are we first */
+ if (shtwo == NULL) /* Yes! */
+ return;
+ else { /* :-( */
+ shone = shpnt;
+ }
+ } else {
+ if (!shtwo) {
+ /* were we here before? xchng first */
+ shtwo = shpnt;
+ }
+ }
+ break;
+ }
+ }
+ /*
+ * Have we got the boot host and one which does not have the bios
+ * enabled.
+ */
+ if (shone && shtwo)
+ break;
+ }
+ if (shone && shtwo) {
+ mega_swap_hosts (shone, shtwo);
+ }
+
+ return;
+}
+
+static void mega_swap_hosts (struct Scsi_Host *shone, struct Scsi_Host *shtwo)
+{
+ struct Scsi_Host *prevtoshtwo;
+ struct Scsi_Host *prevtoshone;
+ struct Scsi_Host *save = NULL;;
+
+ /* Are these two nodes adjacent */
+ if (shtwo->next == shone) {
+
+ if (shtwo == scsi_hostlist && shone->next == NULL) {
+
+ /* just two nodes */
+ scsi_hostlist = shone;
+ shone->next = shtwo;
+ shtwo->next = NULL;
+ } else if (shtwo == scsi_hostlist) {
+ /* first two nodes of the list */
+
+ scsi_hostlist = shone;
+ shtwo->next = shone->next;
+ scsi_hostlist->next = shtwo;
+ } else if (shone->next == NULL) {
+ /* last two nodes of the list */
+
+ prevtoshtwo = scsi_hostlist;
+
+ while (prevtoshtwo->next != shtwo)
+ prevtoshtwo = prevtoshtwo->next;
+
+ prevtoshtwo->next = shone;
+ shone->next = shtwo;
+ shtwo->next = NULL;
+ } else {
+ prevtoshtwo = scsi_hostlist;
+
+ while (prevtoshtwo->next != shtwo)
+ prevtoshtwo = prevtoshtwo->next;
+
+ prevtoshtwo->next = shone;
+ shtwo->next = shone->next;
+ shone->next = shtwo;
+ }
+
+ } else if (shtwo == scsi_hostlist && shone->next == NULL) {
+ /* shtwo at head, shone at tail, not adjacent */
+
+ prevtoshone = scsi_hostlist;
+
+ while (prevtoshone->next != shone)
+ prevtoshone = prevtoshone->next;
+
+ scsi_hostlist = shone;
+ shone->next = shtwo->next;
+ prevtoshone->next = shtwo;
+ shtwo->next = NULL;
+ } else if (shtwo == scsi_hostlist && shone->next != NULL) {
+ /* shtwo at head, shone is not at tail */
+
+ prevtoshone = scsi_hostlist;
+ while (prevtoshone->next != shone)
+ prevtoshone = prevtoshone->next;
+
+ scsi_hostlist = shone;
+ prevtoshone->next = shtwo;
+ save = shtwo->next;
+ shtwo->next = shone->next;
+ shone->next = save;
+ } else if (shone->next == NULL) {
+ /* shtwo not at head, shone at tail */
+
+ prevtoshtwo = scsi_hostlist;
+ prevtoshone = scsi_hostlist;
+
+ while (prevtoshtwo->next != shtwo)
+ prevtoshtwo = prevtoshtwo->next;
+ while (prevtoshone->next != shone)
+ prevtoshone = prevtoshone->next;
+
+ prevtoshtwo->next = shone;
+ shone->next = shtwo->next;
+ prevtoshone->next = shtwo;
+ shtwo->next = NULL;
+
+ } else {
+ prevtoshtwo = scsi_hostlist;
+ prevtoshone = scsi_hostlist;
+ save = NULL;;
+
+ while (prevtoshtwo->next != shtwo)
+ prevtoshtwo = prevtoshtwo->next;
+ while (prevtoshone->next != shone)
+ prevtoshone = prevtoshone->next;
+
+ prevtoshtwo->next = shone;
+ save = shone->next;
+ shone->next = shtwo->next;
+ prevtoshone->next = shtwo;
+ shtwo->next = save;
+ }
+ return;
+}
+
+static inline void mega_freeSgList (mega_host_config * megaCfg)
+{
+ int i;
+
+ for (i = 0; i < megaCfg->max_cmds; i++) {
+ if (megaCfg->scbList[i].sgList)
+ pci_free_consistent (megaCfg->dev,
+ sizeof (mega_64sglist) *
+ MAX_SGLIST,
+ megaCfg->scbList[i].sgList,
+ megaCfg->scbList[i].
+ dma_sghandle64);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x020400 */
+ kfree (megaCfg->scbList[i].sgList); /* free sgList */
+#endif
+ }
+}
+
+/*----------------------------------------------
+ * Get information about the card/driver
+ *----------------------------------------------*/
+const char *megaraid_info (struct Scsi_Host *pSHost)
+{
+ static char buffer[512];
+ mega_host_config *megaCfg;
+
+ megaCfg = (mega_host_config *) pSHost->hostdata;
+
+ sprintf (buffer,
+ "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns",
+ megaCfg->fwVer, megaCfg->productInfo.MaxConcCmds,
+ megaCfg->host->max_id-1, megaCfg->host->max_channel,
+ megaCfg->host->max_lun);
+ return buffer;
+}
+
+/*-----------------------------------------------------------------
+ * Perform a SCSI command
+ * Mailbox area:
+ * 00 01 command
+ * 01 01 command id
+ * 02 02 # of sectors
+ * 04 04 logical bus address
+ * 08 04 physical buffer address
+ * 0C 01 logical drive #
+ * 0D 01 length of scatter/gather list
+ * 0E 01 reserved
+ * 0F 01 mailbox busy
+ * 10 01 numstatus byte
+ * 11 01 status byte
+ *-----------------------------------------------------------------*/
+int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *))
+{
+ DRIVER_LOCK_T mega_host_config * megaCfg;
+ mega_scb *pScb;
+ char *user_area = NULL;
+
+ megaCfg = (mega_host_config *) SCpnt->host->hostdata;
+ DRIVER_LOCK (megaCfg);
+
+#if 0
+ if (!(megaCfg->flag & (1L << SCpnt->channel))) {
+ if (SCpnt->channel < megaCfg->productInfo.SCSIChanPresent)
+ printk ( KERN_NOTICE
+ "scsi%d: scanning channel %d for devices.\n",
+ megaCfg->host->host_no, SCpnt->channel);
+ else
+ printk ( KERN_NOTICE
+ "scsi%d: scanning virtual channel %d for logical drives.\n",
+ megaCfg->host->host_no,
+ SCpnt->channel-megaCfg->productInfo.SCSIChanPresent+1);
+
+ megaCfg->flag |= (1L << SCpnt->channel);
+ }
+#endif
+
+ SCpnt->scsi_done = pktComp;
+
+ if (mega_driver_ioctl (megaCfg, SCpnt))
+ return 0;
+
+ /* If driver in abort or reset.. cancel this command */
+ if (megaCfg->flag & IN_ABORT) {
+ SCpnt->result = (DID_ABORT << 16);
+ /* Add Scsi_Command to end of completed queue */
+ if (megaCfg->qCompletedH == NULL) {
+ megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt;
+ } else {
+ megaCfg->qCompletedT->host_scribble =
+ (unsigned char *) SCpnt;
+ megaCfg->qCompletedT = SCpnt;
+ }
+ megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL;
+ megaCfg->qCcnt++;
+
+ DRIVER_UNLOCK (megaCfg);
+ return 0;
+ } else if (megaCfg->flag & IN_RESET) {
+ SCpnt->result = (DID_RESET << 16);
+ /* Add Scsi_Command to end of completed queue */
+ if (megaCfg->qCompletedH == NULL) {
+ megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt;
+ } else {
+ megaCfg->qCompletedT->host_scribble =
+ (unsigned char *) SCpnt;
+ megaCfg->qCompletedT = SCpnt;
+ }
+ megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL;
+ megaCfg->qCcnt++;
+
+ DRIVER_UNLOCK (megaCfg);
+ return 0;
+ }
+
+ megaCfg->flag |= IN_QUEUE;
+ /* Allocate and build a SCB request */
+ if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) {
+
+ /*
+ * Check if the HBA is in quiescent state, e.g., during a delete
+ * logical drive opertion. If it is, queue the commands in the
+ * internal queue until the delete operation is complete.
+ */
+ if( ! megaCfg->quiescent ) {
+ /* Add SCB to the head of the pending queue */
+ if (megaCfg->qPendingH == NULL) {
+ megaCfg->qPendingH = megaCfg->qPendingT = pScb;
+ } else {
+ megaCfg->qPendingT->next = pScb;
+ megaCfg->qPendingT = pScb;
+ }
+ megaCfg->qPendingT->next = NULL;
+ megaCfg->qPcnt++;
+
+ if (mega_runpendq (megaCfg) == -1) {
+ DRIVER_UNLOCK (megaCfg);
+ return 0;
+ }
+ }
+ else {
+ /* Add SCB to the internal queue */
+ if (megaCfg->int_qh == NULL) {
+ megaCfg->int_qh = megaCfg->int_qt = pScb;
+ } else {
+ megaCfg->int_qt->next = pScb;
+ megaCfg->int_qt = pScb;
+ }
+ megaCfg->int_qt->next = NULL;
+ megaCfg->int_qlen++;
+ }
+
+ if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) {
+#if XENO_KILLED
+ init_MUTEX_LOCKED (&pScb->ioctl_sem);
+#endif
+ spin_unlock_irq (&io_request_lock);
+#if XENO_KILLED
+ down (&pScb->ioctl_sem);
+#endif
+ user_area = (char *)*((u32*)&pScb->SCpnt->cmnd[4]);
+ if (copy_to_user
+ (user_area, pScb->buff_ptr, pScb->iDataSize)) {
+ printk
+ ("megaraid: Error copying ioctl return value to user buffer.\n");
+ pScb->SCpnt->result = (DID_ERROR << 16);
+ }
+ spin_lock_irq (&io_request_lock);
+ DRIVER_LOCK (megaCfg);
+ kfree (pScb->buff_ptr);
+ pScb->buff_ptr = NULL;
+ mega_cmd_done (megaCfg, pScb, pScb->SCpnt->result);
+ mega_rundoneq (megaCfg);
+ mega_runpendq (megaCfg);
+ DRIVER_UNLOCK (megaCfg);
+ }
+
+ megaCfg->flag &= ~IN_QUEUE;
+
+ }
+
+ DRIVER_UNLOCK (megaCfg);
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ * Issue a blocking command to the controller
+ *----------------------------------------------------------------------*/
+#if XENO_KILLED
+volatile static int internal_done_flag = 0;
+volatile static int internal_done_errcode = 0;
+
+static DECLARE_WAIT_QUEUE_HEAD (internal_wait);
+
+static void internal_done (Scsi_Cmnd * SCpnt)
+{
+ internal_done_errcode = SCpnt->result;
+ internal_done_flag++;
+ wake_up (&internal_wait);
+}
+
+/* shouldn't be used, but included for completeness */
+
+int megaraid_command (Scsi_Cmnd * SCpnt)
+{
+ internal_done_flag = 0;
+
+ /* Queue command, and wait until it has completed */
+ megaraid_queue (SCpnt, internal_done);
+
+ while (!internal_done_flag) {
+ interruptible_sleep_on (&internal_wait);
+ }
+
+ return internal_done_errcode;
+}
+#endif
+
+/*---------------------------------------------------------------------
+ * Abort a previous SCSI request
+ *---------------------------------------------------------------------*/
+int megaraid_abort (Scsi_Cmnd * SCpnt)
+{
+ mega_host_config *megaCfg;
+ int rc; /*, idx; */
+ mega_scb *pScb;
+
+ rc = SCSI_ABORT_NOT_RUNNING;
+
+ megaCfg = (mega_host_config *) SCpnt->host->hostdata;
+
+ megaCfg->flag |= IN_ABORT;
+
+ for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) {
+ if (pScb->SCpnt == SCpnt) {
+ /* Found an aborting command */
+#if DEBUG
+ showMbox (pScb);
+#endif
+
+ /*
+ * If the command is queued to be issued to the firmware, abort the scsi cmd,
+ * If the command is already aborted in a previous call to the _abort entry
+ * point, return SCSI_ABORT_SNOOZE, suggesting a reset.
+ * If the command is issued to the firmware, which might complete after
+ * some time, we will mark the scb as aborted, and return to the mid layer,
+ * that abort could not be done.
+ * In the ISR, when this command actually completes, we will perform a normal
+ * completion.
+ *
+ * Oct 27, 1999
+ */
+
+ switch (pScb->state) {
+ case SCB_ABORTED: /* Already aborted */
+ rc = SCSI_ABORT_SNOOZE;
+ break;
+ case SCB_ISSUED: /* Waiting on ISR result */
+ rc = SCSI_ABORT_NOT_RUNNING;
+ pScb->state = SCB_ABORTED;
+ break;
+ case SCB_ACTIVE: /* still on the pending queue */
+ mega_freeSCB (megaCfg, pScb);
+ SCpnt->result = (DID_ABORT << 16);
+ if (megaCfg->qCompletedH == NULL) {
+ megaCfg->qCompletedH =
+ megaCfg->qCompletedT = SCpnt;
+ } else {
+ megaCfg->qCompletedT->host_scribble =
+ (unsigned char *) SCpnt;
+ megaCfg->qCompletedT = SCpnt;
+ }
+ megaCfg->qCompletedT->host_scribble =
+ (unsigned char *) NULL;
+ megaCfg->qCcnt++;
+ rc = SCSI_ABORT_SUCCESS;
+ break;
+ default:
+ printk
+ ("megaraid_abort: unknown command state!!\n");
+ rc = SCSI_ABORT_NOT_RUNNING;
+ break;
+ }
+ break;
+ }
+ }
+
+ megaCfg->flag &= ~IN_ABORT;
+
+#if DEBUG
+ if (megaCfg->flag & IN_QUEUE)
+ printk ("ma:flag is in queue\n");
+ if (megaCfg->qCompletedH == NULL)
+ printk ("ma:qchead == null\n");
+#endif
+
+ /*
+ * This is required here to complete any completed requests to be communicated
+ * over to the mid layer.
+ * Calling just mega_rundoneq() did not work.
+ */
+ if (megaCfg->qCompletedH) {
+ SCpnt = megaCfg->qCompletedH;
+ megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble;
+ megaCfg->qCcnt--;
+
+ SCpnt->host_scribble = (unsigned char *) NULL;
+ /* Callback */
+ callDone (SCpnt);
+ }
+ mega_rundoneq (megaCfg);
+
+ return rc;
+}
+
+/*---------------------------------------------------------------------
+ * Reset a previous SCSI request
+ *---------------------------------------------------------------------*/
+
+int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags)
+{
+ mega_host_config *megaCfg;
+ int idx;
+ int rc;
+ mega_scb *pScb;
+
+ rc = SCSI_RESET_NOT_RUNNING;
+ megaCfg = (mega_host_config *) SCpnt->host->hostdata;
+
+ megaCfg->flag |= IN_RESET;
+
+ printk
+ ("megaraid_RESET: %.08lx cmd=%.02x <c=%d.t=%d.l=%d>, flag = %x\n",
+ SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel,
+ SCpnt->target, SCpnt->lun, rstflags);
+
+ TRACE (("RESET: %.08lx %.02x <%d.%d.%d>\n",
+ SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel,
+ SCpnt->target, SCpnt->lun));
+
+ /*
+ * Walk list of SCBs for any that are still outstanding
+ */
+ for (idx = 0; idx < megaCfg->max_cmds; idx++) {
+ if (megaCfg->scbList[idx].state != SCB_FREE) {
+ SCpnt = megaCfg->scbList[idx].SCpnt;
+ pScb = &megaCfg->scbList[idx];
+ if (SCpnt != NULL) {
+ pScb->state = SCB_RESET;
+ break;
+ }
+ }
+ }
+
+ megaCfg->flag &= ~IN_RESET;
+
+ mega_rundoneq (megaCfg);
+ return rc;
+}
+
+#ifdef CONFIG_PROC_FS
+/* Following code handles /proc fs */
+static int proc_printf (mega_host_config * megaCfg, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ if (megaCfg->procidx > PROCBUFSIZE)
+ return 0;
+
+ va_start (args, fmt);
+ i = vsprintf ((megaCfg->procbuf + megaCfg->procidx), fmt, args);
+ va_end (args);
+
+ megaCfg->procidx += i;
+ return i;
+}
+
+static int proc_read_config (char *page, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+
+ mega_host_config *megaCfg = (mega_host_config *) data;
+
+ *start = page;
+
+ if (megaCfg->productInfo.ProductName[0] != 0)
+ proc_printf (megaCfg, "%s\n", megaCfg->productInfo.ProductName);
+
+ proc_printf (megaCfg, "Controller Type: ");
+
+ if (megaCfg->flag & BOARD_QUARTZ)
+ proc_printf (megaCfg, "438/466/467/471/493\n");
+ else
+ proc_printf (megaCfg, "418/428/434\n");
+
+ if (megaCfg->flag & BOARD_40LD)
+ proc_printf (megaCfg,
+ "Controller Supports 40 Logical Drives\n");
+
+ if (megaCfg->flag & BOARD_64BIT)
+ proc_printf (megaCfg,
+ "Controller / Driver uses 64 bit memory addressing\n");
+
+ proc_printf (megaCfg, "Base = %08x, Irq = %d, ", megaCfg->base,
+ megaCfg->host->irq);
+
+ proc_printf (megaCfg, "Logical Drives = %d, Channels = %d\n",
+ megaCfg->numldrv, megaCfg->productInfo.SCSIChanPresent);
+
+ proc_printf (megaCfg, "Version =%s:%s, DRAM = %dMb\n",
+ megaCfg->fwVer, megaCfg->biosVer,
+ megaCfg->productInfo.DramSize);
+
+ proc_printf (megaCfg,
+ "Controller Queue Depth = %d, Driver Queue Depth = %d\n",
+ megaCfg->productInfo.MaxConcCmds, megaCfg->max_cmds);
+ COPY_BACK;
+ return count;
+}
+
+static int proc_read_stat (char *page, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int i;
+ mega_host_config *megaCfg = (mega_host_config *) data;
+
+ *start = page;
+
+ proc_printf (megaCfg, "Statistical Information for this controller\n");
+ proc_printf (megaCfg, "Interrupts Collected = %lu\n",
+ megaCfg->nInterrupts);
+
+ for (i = 0; i < megaCfg->numldrv; i++) {
+ proc_printf (megaCfg, "Logical Drive %d:\n", i);
+
+ proc_printf (megaCfg,
+ "\tReads Issued = %lu, Writes Issued = %lu\n",
+ megaCfg->nReads[i], megaCfg->nWrites[i]);
+
+ proc_printf (megaCfg,
+ "\tSectors Read = %lu, Sectors Written = %lu\n\n",
+ megaCfg->nReadBlocks[i], megaCfg->nWriteBlocks[i]);
+
+ }
+
+ COPY_BACK;
+ return count;
+}
+
+static int proc_read_status (char *page, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ mega_host_config *megaCfg = (mega_host_config *) data;
+ *start = page;
+
+ proc_printf (megaCfg, "TBD\n");
+ COPY_BACK;
+ return count;
+}
+
+static int proc_read_mbox (char *page, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+
+ mega_host_config *megaCfg = (mega_host_config *) data;
+ volatile mega_mailbox *mbox = megaCfg->mbox;
+
+ *start = page;
+
+ proc_printf (megaCfg, "Contents of Mail Box Structure\n");
+ proc_printf (megaCfg, " Fw Command = 0x%02x\n", mbox->cmd);
+ proc_printf (megaCfg, " Cmd Sequence = 0x%02x\n", mbox->cmdid);
+ proc_printf (megaCfg, " No of Sectors= %04d\n", mbox->numsectors);
+ proc_printf (megaCfg, " LBA = 0x%02x\n", mbox->lba);
+ proc_printf (megaCfg, " DTA = 0x%08x\n", mbox->xferaddr);
+ proc_printf (megaCfg, " Logical Drive= 0x%02x\n", mbox->logdrv);
+ proc_printf (megaCfg, " No of SG Elmt= 0x%02x\n", mbox->numsgelements);
+ proc_printf (megaCfg, " Busy = %01x\n", mbox->busy);
+ proc_printf (megaCfg, " Status = 0x%02x\n", mbox->status);
+
+ /* proc_printf(megaCfg, "Dump of MailBox\n");
+ for (i = 0; i < 16; i++)
+ proc_printf(megaCfg, "%02x ",*(mbox + i));
+
+ proc_printf(megaCfg, "\n\nNumber of Status = %02d\n",mbox->numstatus);
+
+ for (i = 0; i < 46; i++) {
+ proc_printf(megaCfg,"%02d ",*(mbox + 16 + i));
+ if (i%16)
+ proc_printf(megaCfg,"\n");
+ }
+
+ if (!mbox->numsgelements) {
+ dta = phys_to_virt(mbox->xferaddr);
+ for (i = 0; i < mbox->numsgelements; i++)
+ if (dta) {
+ proc_printf(megaCfg,"Addr = %08x\n", (ulong)*(dta + i)); proc_printf(megaCfg,"Length = %08x\n",
+ (ulong)*(dta + i + 4));
+ }
+ }*/
+ COPY_BACK;
+ return count;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */
+#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string, \
+ S_IRUSR | S_IFREG,\
+ controller_proc_dir_entry,\
+ fxn, megaCfg)
+#else
+#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string,S_IRUSR | S_IFREG, controller_proc_dir_entry, fxn, megaCfg)
+
+static struct proc_dir_entry *
+create_proc_read_entry (const char *string,
+ int mode,
+ struct proc_dir_entry *parent,
+ read_proc_t * fxn, mega_host_config * megaCfg)
+{
+ struct proc_dir_entry *temp = NULL;
+
+ temp = kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL);
+ if (!temp)
+ return NULL;
+ memset (temp, 0, sizeof (struct proc_dir_entry));
+
+ if ((temp->name = kmalloc (strlen (string) + 1, GFP_KERNEL)) == NULL) {
+ kfree (temp);
+ return NULL;
+ }
+
+ strcpy ((char *) temp->name, string);
+ temp->namelen = strlen (string);
+ temp->mode = mode; /*S_IFREG | S_IRUSR */ ;
+ temp->data = (void *) megaCfg;
+ temp->read_proc = fxn;
+ proc_register (parent, temp);
+ return temp;
+}
+#endif
+
+static void mega_create_proc_entry (int index, struct proc_dir_entry *parent)
+{
+ u_char string[64] = { 0 };
+ mega_host_config *megaCfg = megaCtlrs[index];
+ struct proc_dir_entry *controller_proc_dir_entry = NULL;
+
+ sprintf (string, "%d", index);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */
+ controller_proc_dir_entry =
+ megaCfg->controller_proc_dir_entry = proc_mkdir (string, parent);
+#else
+ controller_proc_dir_entry =
+ megaCfg->controller_proc_dir_entry =
+ create_proc_entry (string, S_IFDIR | S_IRUGO | S_IXUGO, parent);
+#endif
+
+ if (!controller_proc_dir_entry)
+ printk ("\nmegaraid: proc_mkdir failed\n");
+ else {
+ megaCfg->proc_read =
+ CREATE_READ_PROC ("config", proc_read_config);
+ megaCfg->proc_status =
+ CREATE_READ_PROC ("status", proc_read_status);
+ megaCfg->proc_stat = CREATE_READ_PROC ("stat", proc_read_stat);
+ megaCfg->proc_mbox =
+ CREATE_READ_PROC ("mailbox", proc_read_mbox);
+ }
+
+}
+#endif /* CONFIG_PROC_FS */
+
+/*-------------------------------------------------------------
+ * Return the disk geometry for a particular disk
+ * Input:
+ * Disk *disk - Disk geometry
+ * kdev_t dev - Device node
+ * int *geom - Returns geometry fields
+ * geom[0] = heads
+ * geom[1] = sectors
+ * geom[2] = cylinders
+ *-------------------------------------------------------------*/
+int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom)
+{
+ int heads, sectors, cylinders;
+ mega_host_config *megaCfg;
+
+ /* Get pointer to host config structure */
+ megaCfg = (mega_host_config *) disk->device->host->hostdata;
+
+ if( IS_RAID_CH(megaCfg, disk->device->channel)) {
+ /* Default heads (64) & sectors (32) */
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ /* Handle extended translation size for logical drives > 1Gb */
+ if (disk->capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+
+ /* return result */
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ }
+ else {
+ if( mega_partsize(disk, dev, geom) == 0 ) return 0;
+
+ printk(KERN_WARNING
+ "megaraid: invalid partition on this disk on channel %d\n",
+ disk->device->channel);
+
+ /* Default heads (64) & sectors (32) */
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ /* Handle extended translation size for logical drives > 1Gb */
+ if (disk->capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (heads * sectors);
+ }
+
+ /* return result */
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+ }
+
+ return 0;
+}
+
+/*
+ * Function : static int mega_partsize(Disk * disk, kdev_t dev, int *geom)
+ *
+ * Purpose : to determine the BIOS mapping used to create the partition
+ * table, storing the results (cyls, hds, and secs) in geom
+ *
+ * Note: Code is picked from scsicam.h
+ *
+ * Returns : -1 on failure, 0 on success.
+ */
+static int
+mega_partsize(Disk * disk, kdev_t dev, int *geom)
+{
+#if XENO_KILLED
+ struct buffer_head *bh;
+ struct partition *p, *largest = NULL;
+ int i, largest_cyl;
+ int heads, cyls, sectors;
+ int capacity = disk->capacity;
+
+ int ma = MAJOR(dev);
+ int mi = (MINOR(dev) & ~0xf);
+
+ int block = 1024;
+
+
+ if(blksize_size[ma])
+ block = blksize_size[ma][mi];
+
+ if(!(bh = bread(MKDEV(ma,mi), 0, block)))
+ return -1;
+
+ if( *(unsigned short *)(bh->b_data + 510) == 0xAA55 ) {
+
+ for( largest_cyl = -1, p = (struct partition *)(0x1BE + bh->b_data),
+ i = 0; i < 4; ++i, ++p) {
+
+ if (!p->sys_ind) continue;
+
+ cyls = p->end_cyl + ((p->end_sector & 0xc0) << 2);
+
+ if(cyls >= largest_cyl) {
+ largest_cyl = cyls;
+ largest = p;
+ }
+ }
+ }
+
+ if (largest) {
+ heads = largest->end_head + 1;
+ sectors = largest->end_sector & 0x3f;
+
+ if (heads == 0 || sectors == 0) {
+ brelse(bh);
+ return -1;
+ }
+
+ cyls = capacity/(heads * sectors);
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cyls;
+
+ brelse(bh);
+ return 0;
+ }
+
+ brelse(bh);
+#endif
+ return -1;
+}
+
+
+/*
+ * This routine will be called when the use has done a forced shutdown on the
+ * system. Flush the Adapter cache, that's the most we can do.
+ */
+static int megaraid_reboot_notify (struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ struct Scsi_Host *pSHost;
+ mega_host_config *megaCfg;
+ mega_mailbox *mbox;
+ u_char mboxData[16];
+ int i;
+
+ if (code == SYS_DOWN || code == SYS_HALT) {
+ for (i = 0; i < numCtlrs; i++) {
+ pSHost = megaCtlrs[i]->host;
+
+ megaCfg = (mega_host_config *) pSHost->hostdata;
+ mbox = (mega_mailbox *) mboxData;
+
+ /* Flush cache to disk */
+ memset (mbox, 0, 16);
+ mboxData[0] = 0xA;
+
+ /*
+ * Free irq, otherwise extra interrupt is generated
+ */
+ free_irq (megaCfg->host->irq, megaCfg);
+
+ /*
+ * Issue a blocking (interrupts disabled) command to
+ * the card
+ */
+ megaIssueCmd (megaCfg, mboxData, NULL, 0);
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+static int mega_init_scb (mega_host_config * megacfg)
+{
+ int idx;
+
+#if DEBUG
+ if (megacfg->max_cmds >= MAX_COMMANDS) {
+ printk ("megaraid:ctlr max cmds = %x : MAX_CMDS = %x",
+ megacfg->max_cmds, MAX_COMMANDS);
+ }
+#endif
+
+ for (idx = megacfg->max_cmds - 1; idx >= 0; idx--) {
+
+ megacfg->scbList[idx].idx = idx;
+
+ /*
+ * ISR will make this flag zero to indicate the command has been
+ * completed. This is only for user ioctl calls. Rest of the driver
+ * and the mid-layer operations are not connected with this flag.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ megacfg->scbList[idx].sgList =
+ pci_alloc_consistent (megacfg->dev,
+ sizeof (mega_64sglist) * MAX_SGLIST,
+ &(megacfg->scbList[idx].
+ dma_sghandle64));
+
+ megacfg->scbList[idx].sg64List =
+ (mega_64sglist *) megacfg->scbList[idx].sgList;
+#else
+ megacfg->scbList[idx].sgList = kmalloc (sizeof (mega_sglist) * MAX_SGLIST, GFP_ATOMIC | GFP_DMA);
+#endif
+
+ if (megacfg->scbList[idx].sgList == NULL) {
+ printk (KERN_WARNING
+ "Can't allocate sglist for id %d\n", idx);
+ mega_freeSgList (megacfg);
+ return -1;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ megacfg->scbList[idx].pthru = pci_alloc_consistent (megacfg->dev,
+ sizeof (mega_passthru),
+ &(megacfg->scbList[idx].
+ dma_passthruhandle64));
+
+ if (megacfg->scbList[idx].pthru == NULL) {
+ printk (KERN_WARNING
+ "Can't allocate passthru for id %d\n", idx);
+ }
+
+ megacfg->scbList[idx].epthru =
+ pci_alloc_consistent(
+ megacfg->dev, sizeof(mega_ext_passthru),
+ &(megacfg->scbList[idx].dma_ext_passthruhandle64)
+ );
+
+ if (megacfg->scbList[idx].epthru == NULL) {
+ printk (KERN_WARNING
+ "Can't allocate extended passthru for id %d\n", idx);
+ }
+ /*
+ * Allocate a 256 Byte Bounce Buffer for handling INQ/RD_CAPA
+ */
+ megacfg->scbList[idx].bounce_buffer = pci_alloc_consistent (megacfg->dev,
+ 256,
+ &(megacfg->scbList[idx].
+ dma_bounce_buffer));
+
+ if (!megacfg->scbList[idx].bounce_buffer)
+ printk
+ ("megaraid: allocation for bounce buffer failed\n");
+
+ megacfg->scbList[idx].dma_type = M_RD_DMA_TYPE_NONE;
+#endif
+
+ if (idx < MAX_COMMANDS) {
+ /*
+ * Link to free list
+ * lock not required since we are loading the driver, so no
+ * commands possible right now.
+ */
+ enq_scb_freelist (megacfg, &megacfg->scbList[idx],
+ NO_LOCK, INTR_ENB);
+
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Enqueues a SCB
+ */
+static void enq_scb_freelist (mega_host_config * megacfg, mega_scb * scb, int lock,
+ int intr)
+{
+
+ if (lock == INTERNAL_LOCK || intr == INTR_DIS) {
+ if (intr == INTR_DIS)
+ spin_lock_irq (&megacfg->lock_free);
+ else
+ spin_lock (&megacfg->lock_free);
+ }
+
+ scb->state = SCB_FREE;
+ scb->SCpnt = NULL;
+
+ if (megacfg->qFreeH == (mega_scb *) NULL) {
+ megacfg->qFreeH = megacfg->qFreeT = scb;
+ } else {
+ megacfg->qFreeT->next = scb;
+ megacfg->qFreeT = scb;
+ }
+
+ megacfg->qFreeT->next = NULL;
+ megacfg->qFcnt++;
+
+ if (lock == INTERNAL_LOCK || intr == INTR_DIS) {
+ if (intr == INTR_DIS)
+ spin_unlock_irq (&megacfg->lock_free);
+ else
+ spin_unlock (&megacfg->lock_free);
+ }
+}
+
+/*
+ * Routines for the character/ioctl interface to the driver
+ */
+static int megadev_open (struct inode *inode, struct file *filep)
+{
+ MOD_INC_USE_COUNT;
+ return 0; /* success */
+}
+
+static int megadev_ioctl_entry (struct inode *inode, struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = -1;
+
+ /*
+ * We do not allow parallel ioctls to the driver as of now.
+ */
+#if XENO_KILLED // XXX JWS killed the locking!
+ down (&mimd_entry_mtx);
+ ret = megadev_ioctl (inode, filep, cmd, arg);
+ up (&mimd_entry_mtx);
+#else
+ ret = megadev_ioctl (inode, filep, cmd, arg);
+#endif
+
+ return ret;
+
+}
+
+static int megadev_ioctl (struct inode *inode, struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ int adapno;
+ kdev_t dev;
+ u32 inlen;
+ struct uioctl_t ioc;
+ char *kvaddr = NULL;
+ int nadap = numCtlrs;
+ u8 opcode;
+ u32 outlen;
+ int ret;
+ u8 subopcode;
+ Scsi_Cmnd *scsicmd;
+ struct Scsi_Host *shpnt;
+ char *uaddr;
+ struct uioctl_t *uioc;
+ dma_addr_t dma_addr;
+ u32 length;
+ mega_host_config *megacfg = NULL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */
+ struct pci_dev pdev;
+ struct pci_dev *pdevp = &pdev;
+#else
+ char *pdevp = NULL;
+#endif
+ IO_LOCK_T;
+
+ if (!inode || !(dev = inode->i_rdev))
+ return -EINVAL;
+
+ if (_IOC_TYPE (cmd) != MEGAIOC_MAGIC)
+ return (-EINVAL);
+
+ /*
+ * Get the user ioctl structure
+ */
+ ret = verify_area (VERIFY_WRITE, (char *) arg, sizeof (struct uioctl_t));
+
+ if (ret)
+ return ret;
+
+ if(copy_from_user (&ioc, (char *) arg, sizeof (struct uioctl_t)))
+ return -EFAULT;
+
+ /*
+ * The first call the applications should make is to find out the
+ * number of controllers in the system. The next logical call should
+ * be for getting the list of controllers in the system as detected
+ * by the driver.
+ */
+
+ /*
+ * Get the opcode and subopcode for the commands
+ */
+ opcode = ioc.ui.fcs.opcode;
+ subopcode = ioc.ui.fcs.subopcode;
+
+ switch (opcode) {
+ case M_RD_DRIVER_IOCTL_INTERFACE:
+ switch (subopcode) {
+ case MEGAIOC_QDRVRVER: /* Query driver version */
+ put_user (driver_ver, (u32 *) ioc.data);
+ return 0;
+
+ case MEGAIOC_QNADAP: /* Get # of adapters */
+ put_user (nadap, (int *) ioc.data);
+ return nadap;
+
+ case MEGAIOC_QADAPINFO: /* Get adapter information */
+ /*
+ * which adapter?
+ */
+ adapno = ioc.ui.fcs.adapno;
+
+ /*
+ * The adapter numbers do not start with 0, at least in
+ * the user space. This is just to make sure, 0 is not the
+ * default value which will refer to adapter 1. So the
+ * user needs to make use of macros MKADAP() and GETADAP()
+ * (See megaraid.h) while making ioctl() call.
+ */
+ adapno = GETADAP (adapno);
+
+ if (adapno >= numCtlrs)
+ return (-ENODEV);
+
+ ret = verify_area (VERIFY_WRITE,
+ ioc.data,
+ sizeof (struct mcontroller));
+ if (ret)
+ return ret;
+
+ /*
+ * Copy struct mcontroller to user area
+ */
+ if (copy_to_user (ioc.data,
+ mcontroller + adapno,
+ sizeof (struct mcontroller)))
+ return -EFAULT;
+ return 0;
+
+ default:
+ return (-EINVAL);
+
+ } /* inner switch */
+ break;
+
+ case M_RD_IOCTL_CMD_NEW:
+
+ /*
+ * Deletion of logical drives is only handled in 0x80 commands
+ */
+ if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) {
+ return -EINVAL;
+ }
+
+ /* which adapter? */
+ adapno = ioc.ui.fcs.adapno;
+
+ /* See comment above: MEGAIOC_QADAPINFO */
+ adapno = GETADAP(adapno);
+
+ if (adapno >= numCtlrs)
+ return(-ENODEV);
+
+ length = ioc.ui.fcs.length;
+
+ /* Check for zero length buffer or very large buffers */
+ if( !length || length > 32*1024 )
+ return -EINVAL;
+
+ /* save the user address */
+ uaddr = ioc.ui.fcs.buffer;
+
+ /*
+ * For M_RD_IOCTL_CMD_NEW commands, the fields outlen and inlen of
+ * uioctl_t structure are treated as flags. If outlen is 1, the
+ * data is transferred from the device and if inlen is 1, the data
+ * is transferred to the device.
+ */
+ outlen = ioc.outlen;
+ inlen = ioc.inlen;
+
+ if(outlen) {
+ ret = verify_area(VERIFY_WRITE, (char *)ioc.ui.fcs.buffer, length);
+ if (ret) return ret;
+ }
+ if(inlen) {
+ ret = verify_area(VERIFY_READ, (char *) ioc.ui.fcs.buffer, length);
+ if (ret) return ret;
+ }
+
+ /*
+ * Find this host
+ */
+ for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) {
+ if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) {
+ megacfg = (mega_host_config *)shpnt->hostdata;
+ break;
+ }
+ }
+ if(shpnt == NULL) return -ENODEV;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA);
+#else
+ scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd),
+ GFP_ATOMIC | GFP_DMA);
+#endif
+ if(scsicmd == NULL) return -ENOMEM;
+
+ memset(scsicmd, 0, sizeof(Scsi_Cmnd));
+ scsicmd->host = shpnt;
+
+ if( outlen || inlen ) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pdevp = &pdev;
+ memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev));
+ pdevp->dma_mask = 0xffffffff;
+#else
+ pdevp = NULL;
+#endif
+ kvaddr = dma_alloc_consistent(pdevp, length, &dma_addr);
+
+ if( kvaddr == NULL ) {
+ printk(KERN_WARNING "megaraid:allocation failed\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */
+ kfree(scsicmd);
+#else
+ scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
+#endif
+ return -ENOMEM;
+ }
+
+ ioc.ui.fcs.buffer = kvaddr;
+
+ if (inlen) {
+ /* copyin the user data */
+ copy_from_user(kvaddr, (char *)uaddr, length );
+ }
+ }
+
+ scsicmd->cmnd[0] = MEGADEVIOC;
+ scsicmd->request_buffer = (void *)&ioc;
+
+#if XENO_KILLED
+ init_MUTEX_LOCKED(&mimd_ioctl_sem);
+#endif
+
+ IO_LOCK;
+ megaraid_queue(scsicmd, megadev_ioctl_done);
+
+ IO_UNLOCK;
+#if XENO_KILLED
+ down(&mimd_ioctl_sem);
+#endif
+ if( !scsicmd->result && outlen ) {
+ if (copy_to_user(uaddr, kvaddr, length))
+ return -EFAULT;
+ }
+
+ /*
+ * copyout the result
+ */
+ uioc = (struct uioctl_t *)arg;
+
+ if( ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU ) {
+ put_user( scsicmd->result, &uioc->pthru.scsistatus );
+ if (copy_to_user( uioc->pthru.reqsensearea, scsicmd->sense_buffer,
+ MAX_REQ_SENSE_LEN ));
+ ret= -EFAULT;
+ } else {
+ put_user(1, &uioc->mbox[16]); /* numstatus */
+ /* status */
+ put_user (scsicmd->result, &uioc->mbox[17]);
+ }
+
+ if (kvaddr) {
+ dma_free_consistent(pdevp, length, kvaddr, dma_addr);
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */
+ kfree (scsicmd);
+#else
+ scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
+#endif
+
+ /* restore the user address */
+ ioc.ui.fcs.buffer = uaddr;
+
+ return ret;
+
+ case M_RD_IOCTL_CMD:
+ /* which adapter? */
+ adapno = ioc.ui.fcs.adapno;
+
+ /* See comment above: MEGAIOC_QADAPINFO */
+ adapno = GETADAP (adapno);
+
+ if (adapno >= numCtlrs)
+ return (-ENODEV);
+
+ /* save the user address */
+ uaddr = ioc.data;
+ outlen = ioc.outlen;
+ inlen = ioc.inlen;
+
+ if ((outlen >= IOCTL_MAX_DATALEN) || (inlen >= IOCTL_MAX_DATALEN))
+ return (-EINVAL);
+
+ if (outlen) {
+ ret = verify_area (VERIFY_WRITE, ioc.data, outlen);
+ if (ret) return ret;
+ }
+ if (inlen) {
+ ret = verify_area (VERIFY_READ, ioc.data, inlen);
+ if (ret) return ret;
+ }
+
+ /*
+ * Find this host
+ */
+ for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) {
+ if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) {
+ megacfg = (mega_host_config *)shpnt->hostdata;
+ break;
+ }
+ }
+ if(shpnt == NULL) return -ENODEV;
+
+ /*
+ * ioctls for deleting logical drives is a special case, so check
+ * for it first
+ */
+ if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) {
+
+ if( !megacfg->support_random_del ) {
+ printk("megaraid: logdrv delete on non supporting f/w.\n");
+ return -EINVAL;
+ }
+
+ uioc = (struct uioctl_t *)arg;
+
+ ret = mega_del_logdrv(megacfg, ioc.mbox[3]);
+
+ put_user(1, &uioc->mbox[16]); /* numstatus */
+ put_user(ret, &uioc->mbox[17]); /* status */
+
+ /* if deletion failed, let the user know by failing ioctl */
+ return ret;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL|GFP_DMA);
+#else
+ scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd),
+ GFP_ATOMIC | GFP_DMA);
+#endif
+ if(scsicmd == NULL) return -ENOMEM;
+
+ memset(scsicmd, 0, sizeof(Scsi_Cmnd));
+ scsicmd->host = shpnt;
+
+ if (outlen || inlen) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pdevp = &pdev;
+ memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev));
+ pdevp->dma_mask = 0xffffffff;
+#else
+ pdevp = NULL;
+#endif
+ /*
+ * Allocate a page of kernel space.
+ */
+ kvaddr = dma_alloc_consistent(pdevp, PAGE_SIZE, &dma_addr);
+
+ if( kvaddr == NULL ) {
+ printk (KERN_WARNING "megaraid:allocation failed\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */
+ kfree(scsicmd);
+#else
+ scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
+#endif
+ return -ENOMEM;
+ }
+
+ ioc.data = kvaddr;
+
+ if (inlen) {
+ if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
+ /* copyin the user data */
+ copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen);
+ } else {
+ copy_from_user (kvaddr, uaddr, inlen);
+ }
+ }
+ }
+
+ scsicmd->cmnd[0] = MEGADEVIOC;
+ scsicmd->request_buffer = (void *) &ioc;
+
+#if XENO_KILLED
+ init_MUTEX_LOCKED (&mimd_ioctl_sem);
+#endif
+
+ IO_LOCK;
+ megaraid_queue (scsicmd, megadev_ioctl_done);
+
+ IO_UNLOCK;
+#if XENO_KILLED
+ down (&mimd_ioctl_sem);
+#endif
+
+ if (!scsicmd->result && outlen) {
+ if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
+ if (copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen))
+ ret = -EFAULT;
+ } else {
+ if (copy_to_user (uaddr, kvaddr, outlen))
+ ret = -EFAULT;
+ }
+ }
+
+ /*
+ * copyout the result
+ */
+ uioc = (struct uioctl_t *) arg;
+
+ if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) {
+ put_user (scsicmd->result, &uioc->pthru.scsistatus);
+
+ /*
+ * If scsicmd->result is 0x02 (CHECK CONDITION) then copy the
+ * SCSI sense data into user area
+ */
+ if (copy_to_user( uioc->pthru.reqsensearea, scsicmd->sense_buffer,
+ MAX_REQ_SENSE_LEN ))
+ ret = -EFAULT;
+
+ } else {
+ put_user (1, &uioc->mbox[16]); /* numstatus */
+ put_user (scsicmd->result, &uioc->mbox[17]); /* status */
+ }
+
+ if (kvaddr) {
+ dma_free_consistent(pdevp, PAGE_SIZE, kvaddr, dma_addr );
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ kfree (scsicmd);
+#else
+ scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd));
+#endif
+
+ /* restore user pointer */
+ ioc.data = uaddr;
+
+ return ret;
+
+ default:
+ return (-EINVAL);
+
+ }/* Outer switch */
+
+ return 0;
+}
+
+static void
+megadev_ioctl_done(Scsi_Cmnd *sc)
+{
+#if XENO_KILLED
+ up (&mimd_ioctl_sem);
+#endif
+}
+
+static mega_scb *
+megadev_doioctl (mega_host_config * megacfg, Scsi_Cmnd * sc)
+{
+ u8 cmd;
+ struct uioctl_t *ioc = NULL;
+ mega_mailbox *mbox = NULL;
+ mega_ioctl_mbox *mboxioc = NULL;
+ struct mbox_passthru *mboxpthru = NULL;
+ mega_scb *scb = NULL;
+ mega_passthru *pthru = NULL;
+
+ if ((scb = mega_allocateSCB (megacfg, sc)) == NULL) {
+ sc->result = (DID_ERROR << 16);
+ callDone (sc);
+ return NULL;
+ }
+
+ ioc = (struct uioctl_t *) sc->request_buffer;
+
+ memcpy (scb->mboxData, ioc->mbox, sizeof (scb->mboxData));
+
+ /* The generic mailbox */
+ mbox = (mega_mailbox *) ioc->mbox;
+
+ /*
+ * Get the user command
+ */
+ cmd = ioc->mbox[0];
+
+ switch (cmd) {
+ case MEGA_MBOXCMD_PASSTHRU:
+ /*
+ * prepare the SCB with information from the user ioctl structure
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ pthru = scb->pthru;
+#else
+ pthru = &scb->pthru;
+#endif
+ memcpy (pthru, &ioc->pthru, sizeof (mega_passthru));
+ mboxpthru = (struct mbox_passthru *) scb->mboxData;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (megacfg->flag & BOARD_64BIT) {
+ /* This is just a sample with one element
+ * This if executes onlu on 2.4 kernels
+ */
+ mboxpthru->dataxferaddr = scb->dma_passthruhandle64;
+ scb->sg64List[0].address =
+ pci_map_single (megacfg->dev,
+ ioc->data,
+ 4096, PCI_DMA_BIDIRECTIONAL);
+ scb->sg64List[0].length = 4096; // TODO: Check this
+ pthru->dataxferaddr = scb->dma_sghandle64;
+ pthru->numsgelements = 1;
+ mboxpthru->cmd = MEGA_MBOXCMD_PASSTHRU64;
+ } else {
+ mboxpthru->dataxferaddr = scb->dma_passthruhandle64;
+ pthru->dataxferaddr =
+ pci_map_single (megacfg->dev,
+ ioc->data,
+ 4096, PCI_DMA_BIDIRECTIONAL);
+ pthru->numsgelements = 0;
+ }
+
+#else
+ {
+ mboxpthru->dataxferaddr = virt_to_bus (&scb->pthru);
+ pthru->dataxferaddr = virt_to_bus (ioc->data);
+ pthru->numsgelements = 0;
+ }
+#endif
+
+ pthru->reqsenselen = 14;
+ break;
+
+ default: /* Normal command */
+ mboxioc = (mega_ioctl_mbox *) scb->mboxData;
+
+ if (ioc->ui.fcs.opcode == M_RD_IOCTL_CMD_NEW) {
+ scb->buff_ptr = ioc->ui.fcs.buffer;
+ scb->iDataSize = ioc->ui.fcs.length;
+ } else {
+ scb->buff_ptr = ioc->data;
+ scb->iDataSize = 4096; // TODO:check it
+ }
+
+ set_mbox_xfer_addr (megacfg, scb, mboxioc, FROMTO_DEVICE);
+ mboxioc->numsgelements = 0;
+ break;
+ }
+
+ return scb;
+}
+
+static int
+megadev_close (struct inode *inode, struct file *filep)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return 0;
+}
+
+
+static int
+mega_support_ext_cdb(mega_host_config *this_hba)
+{
+ mega_mailbox *mboxpnt;
+ unsigned char mbox[16];
+ int ret;
+
+ mboxpnt = (mega_mailbox *) mbox;
+
+ memset(mbox, 0, sizeof (mbox));
+ /*
+ * issue command to find out if controller supports extended CDBs.
+ */
+ mbox[0] = 0xA4;
+ mbox[2] = 0x16;
+
+ ret = megaIssueCmd(this_hba, mbox, NULL, 0);
+
+ return !ret;
+}
+
+
+/*
+ * Find out if this controller supports random deletion and addition of
+ * logical drives
+ */
+static int
+mega_support_random_del(mega_host_config *this_hba)
+{
+#if XENO_KILLED
+ mega_mailbox *mboxpnt;
+ unsigned char mbox[16];
+ int ret;
+
+ mboxpnt = (mega_mailbox *)mbox;
+
+ memset(mbox, 0, sizeof(mbox));
+
+ /*
+ * issue command
+ */
+ mbox[0] = FC_DEL_LOGDRV;
+ mbox[2] = OP_SUP_DEL_LOGDRV;
+
+ ret = megaIssueCmd(this_hba, mbox, NULL, 0);
+
+ return !ret;
+#endif
+ return 0; // no support for random deletions
+}
+
+static int
+mega_del_logdrv(mega_host_config *this_hba, int logdrv)
+{
+ return -ENOSYS;
+#if XENO_KILLED
+ int rval;
+ IO_LOCK_T;
+ DECLARE_WAIT_QUEUE_HEAD(wq);
+ mega_scb *scbp;
+
+ /*
+ * Stop sending commands to the controller, queue them internally.
+ * When deletion is complete, ISR will flush the queue.
+ */
+ IO_LOCK;
+ this_hba->quiescent = 1;
+ IO_UNLOCK;
+
+ while( this_hba->qPcnt ) {
+ sleep_on_timeout( &wq, 1*HZ ); /* sleep for 1s */
+ }
+ rval = mega_do_del_logdrv(this_hba, logdrv);
+
+ IO_LOCK;
+ /*
+ * Attach the internal queue to the pending queue
+ */
+ if( this_hba->qPendingH == NULL ) {
+ /*
+ * If pending queue head is null, make internal queue as
+ * pending queue
+ */
+ this_hba->qPendingH = this_hba->int_qh;
+ this_hba->qPendingT = this_hba->int_qt;
+ this_hba->qPcnt = this_hba->int_qlen;
+ }
+ else {
+ /*
+ * Append pending queue to internal queue
+ */
+ if( this_hba->int_qt ) {
+ this_hba->int_qt->next = this_hba->qPendingH;
+
+ this_hba->qPendingH = this_hba->int_qh;
+ this_hba->qPcnt += this_hba->int_qlen;
+ }
+ }
+
+ this_hba->int_qh = this_hba->int_qt = NULL;
+ this_hba->int_qlen = 0;
+
+ /*
+ * If delete operation was successful, add 0x80 to the logical drive
+ * ids for commands in the pending queue.
+ */
+ if( this_hba->read_ldidmap) {
+ for( scbp = this_hba->qPendingH; scbp; scbp = scbp->next ) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if( scbp->pthru->logdrv < 0x80 )
+ scbp->pthru->logdrv += 0x80;
+#else
+ if( scbp->pthru.logdrv < 0x80 )
+ scbp->pthru.logdrv += 0x80;
+#endif
+ }
+ }
+ this_hba->quiescent = 0;
+
+ IO_UNLOCK;
+
+ return rval;
+#endif
+}
+
+
+static int
+mega_do_del_logdrv(mega_host_config *this_hba, int logdrv)
+{
+ mega_mailbox *mboxpnt;
+ unsigned char mbox[16];
+ int rval;
+
+ mboxpnt = (mega_mailbox *)mbox;
+
+ memset(mbox, 0, sizeof(mbox));
+
+ mbox[0] = FC_DEL_LOGDRV;
+ mbox[2] = OP_DEL_LOGDRV;
+ mbox[3] = logdrv;
+
+ rval = megaIssueCmd(this_hba, mbox, NULL, 0);
+
+ /* log this event */
+ if( rval != 0 ) {
+ printk("megaraid: Attempt to delete logical drive %d failed.",
+ logdrv);
+ return rval;
+ }
+
+ printk("megaraid: logical drive %d deleted.\n", logdrv);
+
+ /*
+ * After deleting first logical drive, the logical drives must be
+ * addressed by adding 0x80 to the logical drive id.
+ */
+ this_hba->read_ldidmap = 1;
+
+ return rval;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+void *
+dma_alloc_consistent(void *dev, size_t size, dma_addr_t *dma_addr)
+{
+ void *_tv;
+ int npages;
+ int order = 0;
+
+ /*
+ * How many pages application needs
+ */
+ npages = size / PAGE_SIZE;
+
+ /* Do we need one more page */
+ if(size % PAGE_SIZE)
+ npages++;
+
+ order = mega_get_order(npages);
+
+ _tv = (void *)__get_free_pages(GFP_DMA, order);
+
+ if( _tv != NULL ) {
+ memset(_tv, 0, size);
+ *(dma_addr) = virt_to_bus(_tv);
+ }
+
+ return _tv;
+}
+
+/*
+ * int mega_get_order(int)
+ *
+ * returns the order to be used as 2nd argument to __get_free_pages() - which
+ * return pages equal to pow(2, order) - AM
+ */
+int
+mega_get_order(int n)
+{
+ int i = 0;
+
+ while( pow_2(i++) < n )
+ ; /* null statement */
+
+ return i-1;
+}
+
+/*
+ * int pow_2(int)
+ *
+ * calculates pow(2, i)
+ */
+int
+pow_2(int i)
+{
+ unsigned int v = 1;
+
+ while(i--)
+ v <<= 1;
+
+ return v;
+}
+
+void
+dma_free_consistent(void *dev, size_t size, void *vaddr, dma_addr_t dma_addr)
+{
+ int npages;
+ int order = 0;
+
+ npages = size / PAGE_SIZE;
+
+ if(size % PAGE_SIZE)
+ npages++;
+
+ if (npages == 1)
+ order = 0;
+ else if (npages == 2)
+ order = 1;
+ else if (npages <= 4)
+ order = 2;
+ else
+ order = 3;
+
+ free_pages((unsigned long)vaddr, order);
+
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static
+#endif /* LINUX VERSION 2.4.XX */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) || defined(MODULE)
+Scsi_Host_Template driver_template = MEGARAID;
+
+#include "scsi_module.c.inc"
+#endif /* LINUX VERSION 2.4.XX || MODULE */
+
+/* vi: set ts=4: */
--- /dev/null
+#ifndef __MEGARAID_H__
+#define __MEGARAID_H__
+
+#include <xeno/version.h>
+
+/*
+ * For state flag. Do not use LSB(8 bits) which are
+ * reserved for storing info about channels.
+ */
+#define IN_ISR 0x80000000L
+#define IN_ABORT 0x40000000L
+#define IN_RESET 0x20000000L
+#define IN_QUEUE 0x10000000L
+#define BOARD_QUARTZ 0x08000000L
+#define BOARD_40LD 0x04000000L
+#define BOARD_64BIT 0x02000000L
+
+#define SCB_FREE 0x0
+#define SCB_ACTIVE 0x1
+#define SCB_WAITQ 0x2
+#define SCB_ISSUED 0x3
+#define SCB_COMPLETE 0x4
+#define SCB_ABORTED 0x5
+#define SCB_RESET 0x6
+
+#define M_RD_CRLFSTR "\n"
+#define M_RD_IOCTL_CMD 0x80
+#define M_RD_IOCTL_CMD_NEW 0x81
+#define M_RD_DRIVER_IOCTL_INTERFACE 0x82
+
+#define MEGARAID_VERSION "v1.18d (Release Date: Wed Aug 7 18:51:51 EDT 2002)\n"
+
+#define MEGARAID_IOCTL_VERSION 114
+
+/* Methods */
+#define GET_DRIVER_INFO 0x1
+
+#define MEGA_CMD_TIMEOUT 10
+
+/* Feel free to fiddle with these.. max values are:
+ SGLIST 0..26
+ COMMANDS 0..253
+ CMDPERLUN 0..63
+*/
+
+#define MAX_SGLIST 0x1A
+#define MAX_COMMANDS 127
+#define MAX_CMD_PER_LUN 63
+#define MAX_FIRMWARE_STATUS 46
+
+#define MAX_LOGICAL_DRIVES 8
+#define MAX_CHANNEL 5
+#define MAX_TARGET 15
+#define MAX_PHYSICAL_DRIVES MAX_CHANNEL*MAX_TARGET
+
+#define INQUIRY_DATA_SIZE 0x24
+#define MAX_CDB_LEN 0x0A
+#define MAX_REQ_SENSE_LEN 0x20
+
+#define INTR_VALID 0x40
+
+/* Direction Macros for MBOX Data direction */
+#define TO_DEVICE 0x0
+#define FROM_DEVICE 0x1
+#define FROMTO_DEVICE 0x2
+
+/* Mailbox commands */
+#define MEGA_MBOXCMD_LREAD 0x01
+#define MEGA_MBOXCMD_LWRITE 0x02
+#define MEGA_MBOXCMD_LREAD64 0xA7
+#define MEGA_MBOXCMD_LWRITE64 0xA8
+#define MEGA_MBOXCMD_PASSTHRU 0x03
+#define MEGA_MBOXCMD_EXTPASSTHRU 0xE3
+#define MEGA_MBOXCMD_PASSTHRU64 0xC3
+#define MEGA_MBOXCMD_ADAPTERINQ 0x05
+
+
+/* Offsets into Mailbox */
+#define COMMAND_PORT 0x00
+#define COMMAND_ID_PORT 0x01
+#define SG_LIST_PORT0 0x08
+#define SG_LIST_PORT1 0x09
+#define SG_LIST_PORT2 0x0a
+#define SG_LIST_PORT3 0x0b
+#define SG_ELEMENT_PORT 0x0d
+#define NO_FIRED_PORT 0x0f
+
+/* I/O Port offsets */
+#define I_CMD_PORT 0x00
+#define I_ACK_PORT 0x00
+#define I_TOGGLE_PORT 0x01
+#define INTR_PORT 0x0a
+
+#define MAILBOX_SIZE (sizeof(mega_mailbox)-16)
+#define MBOX_BUSY_PORT 0x00
+#define MBOX_PORT0 0x04
+#define MBOX_PORT1 0x05
+#define MBOX_PORT2 0x06
+#define MBOX_PORT3 0x07
+#define ENABLE_MBOX_REGION 0x0B
+
+/* I/O Port Values */
+#define ISSUE_BYTE 0x10
+#define ACK_BYTE 0x08
+#define ENABLE_INTR_BYTE 0xc0
+#define DISABLE_INTR_BYTE 0x00
+#define VALID_INTR_BYTE 0x40
+#define MBOX_BUSY_BYTE 0x10
+#define ENABLE_MBOX_BYTE 0x00
+
+/* Setup some port macros here */
+#define WRITE_MAILBOX(base,offset,value) *(base+offset)=value
+#define READ_MAILBOX(base,offset) *(base+offset)
+
+#define WRITE_PORT(base,offset,value) outb_p(value,base+offset)
+#define READ_PORT(base,offset) inb_p(base+offset)
+
+#define ISSUE_COMMAND(base) WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE)
+#define CLEAR_INTR(base) WRITE_PORT(base,I_ACK_PORT,ACK_BYTE)
+#define ENABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE)
+#define DISABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE)
+
+#ifndef PCI_VENDOR_ID_LSI_LOGIC
+#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
+#endif
+
+/* Define AMI's PCI codes */
+#ifndef PCI_VENDOR_ID_AMI
+#define PCI_VENDOR_ID_AMI 0x101E
+#endif
+
+#ifndef PCI_DEVICE_ID_AMI_MEGARAID
+#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
+#endif
+
+#ifndef PCI_DEVICE_ID_AMI_MEGARAID2
+#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060
+#endif
+
+#ifndef PCI_DEVICE_ID_AMI_MEGARAID3
+#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960
+#endif
+
+#define PCI_VENDOR_ID_DISCOVERY 0x1028
+#define PCI_DEVICE_ID_DISCOVERY 0x000E
+
+#define PCI_VENDOR_ID_PERC4_DI_YSTONE 0x1028
+#define PCI_DEVICE_ID_PERC4_DI_YSTONE 0x000F
+
+#define PCI_VENDOR_ID_PERC4_QC_VERDE 0x1000
+#define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407
+
+/* Special Adapter Commands */
+#define FW_FIRE_WRITE 0x2C
+#define FW_FIRE_FLASH 0x2D
+
+#define FC_NEW_CONFIG 0xA1
+#define DCMD_FC_CMD 0xA1
+#define DCMD_FC_PROCEED 0x02
+#define DCMD_DELETE_LOGDRV 0x03
+#define DCMD_FC_READ_NVRAM_CONFIG 0x04
+#define DCMD_FC_READ_NVRAM_CONFIG_64 0xC0
+#define DCMD_FC_READ_FINAL_CONFIG 0x05
+#define DCMD_GET_DISK_CONFIG 0x06
+#define DCMD_GET_DISK_CONFIG_64 0xC2
+#define DCMD_CHANGE_LDNO 0x07
+#define DCMD_COMPACT_CONFIG 0x08
+#define DCMD_DELETE_DRIVEGROUP 0x09
+#define DCMD_GET_LOOPID_INFO 0x0A
+#define DCMD_CHANGE_LOOPID 0x0B
+#define DCMD_GET_NUM_SCSI_CHANS 0x0C
+#define DCMD_WRITE_CONFIG 0x0D
+#define DCMD_WRITE_CONFIG_64 0xC1
+
+#define NC_SUBOP_PRODUCT_INFO 0x0E
+#define NC_SUBOP_ENQUIRY3 0x0F
+#define ENQ3_GET_SOLICITED_NOTIFY_ONLY 0x01
+#define ENQ3_GET_SOLICITED_FULL 0x02
+#define ENQ3_GET_UNSOLICITED 0x03
+
+#define PCI_CONF_BASE_ADDR_OFFSET 0x10
+#define PCI_CONF_IRQ_OFFSET 0x3c
+#define PCI_CONF_AMISIG 0xa0
+#define PCI_CONF_AMISIG64 0xa4
+
+/* Sub-System Vendor ID sorted on alphabetical order*/
+#define LSI_SUBSYS_ID 0x1000
+#define AMI_SUBSYS_ID 0x101E
+#define DELL_SUBSYS_ID 0x1028
+#define HP_SUBSYS_ID 0x103C
+
+#define AMI_SIGNATURE 0x3344
+#define AMI_SIGNATURE_471 0xCCCC
+#define AMI_64BIT_SIGNATURE 0x0299
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /*0x20100 */
+#define MEGARAID \
+ { NULL, /* Next */\
+ NULL, /* Usage Count Pointer */\
+ NULL, /* proc Directory Entry */\
+ megaraid_proc_info, /* proc Info Function */\
+ "MegaRAID", /* Driver Name */\
+ megaraid_detect, /* Detect Host Adapter */\
+ megaraid_release, /* Release Host Adapter */\
+ megaraid_info, /* Driver Info Function */\
+ megaraid_command, /* Command Function */\
+ megaraid_queue, /* Queue Command Function */\
+ megaraid_abort, /* Abort Command Function */\
+ megaraid_reset, /* Reset Command Function */\
+ NULL, /* Slave Attach Function */\
+ megaraid_biosparam, /* Disk BIOS Parameters */\
+ MAX_COMMANDS, /* # of cmds that can be\
+ outstanding at any time */\
+ 7, /* HBA Target ID */\
+ MAX_SGLIST, /* Scatter/Gather Table Size */\
+ MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\
+ 0, /* Present */\
+ 0, /* Default Unchecked ISA DMA */\
+ ENABLE_CLUSTERING } /* Enable Clustering */
+#else
+#define MEGARAID \
+ {\
+ name: "MegaRAID", /* Driver Name */\
+ detect: megaraid_detect, /* Detect Host Adapter */\
+ release: megaraid_release, /* Release Host Adapter */\
+ info: megaraid_info, /* Driver Info Function */\
+ command: NULL, /* XEN KILLED */\
+ queuecommand: megaraid_queue, /* Queue Command Function */\
+ abort: megaraid_abort, /* Abort Command Function */\
+ reset: megaraid_reset, /* Reset Command Function */\
+ bios_param: megaraid_biosparam, /* Disk BIOS Parameters */\
+ can_queue: MAX_COMMANDS, /* Can Queue */\
+ this_id: 7, /* HBA Target ID */\
+ sg_tablesize: MAX_SGLIST, /* Scatter/Gather Table Size */\
+ cmd_per_lun: MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\
+ present: 0, /* Present */\
+ unchecked_isa_dma: 0, /* Default Unchecked ISA DMA */\
+ use_clustering: ENABLE_CLUSTERING, /* Enable Clustering */\
+ }
+#endif
+// XENO: REMOVED command: megaraid_command, /* Command Function */
+// XENO: REMOVED proc_info: megaraid_proc_info, /* /proc driver info */
+// XENO: REMOVED highmem_io: 1, /* enable HIGHMEM I/O */
+
+
+/***********************************************************************
+ * Structure Declarations for the Firmware supporting 40 Logical Drives
+ * and 256 Physical Drives.
+ ***********************************************************************/
+
+#define FC_MAX_LOGICAL_DRIVES 40
+#define FC_MAX_LOG_DEVICES FC_MAX_LOGICAL_DRIVES
+#define FC_MAX_SPAN_DEPTH 8
+#define FC_MAX_ROW_SIZE 32
+
+#define FC_MAX_CHANNELS 16
+#define FC_MAX_TARGETS_PER_CHANNEL 16
+#define FC_MAX_PHYSICAL_DEVICES 256
+
+/********************************************
+ * PRODUCT_INFO
+ ********************************************/
+
+#define SIG_40LOG_32STR_8SPN 0x00282008
+
+/*
+ * Utilities declare this strcture size as 1024 bytes. So more fields can
+ * be added in future.
+ */
+
+struct MRaidProductInfo {
+ u32 DataSize; /* current size in bytes (not including resvd) */
+ u32 ConfigSignature;
+ /* Current value is 0x00282008
+ * 0x28=MAX_LOGICAL_DRIVES,
+ * 0x20=Number of stripes and
+ * 0x08=Number of spans */
+ u8 FwVer[16]; /* printable ASCI string */
+ u8 BiosVer[16]; /* printable ASCI string */
+ u8 ProductName[80]; /* printable ASCI string */
+
+ u8 MaxConcCmds; /* Max. concurrent commands supported */
+ u8 SCSIChanPresent; /* Number of SCSI Channels detected */
+ u8 FCLoopPresent; /* Number of Fibre Loops detected */
+ u8 memType; /* EDO, FPM, SDRAM etc */
+
+ u32 signature;
+ u16 DramSize; /* In terms of MB */
+ u16 subSystemID;
+
+ u16 subSystemVendorID;
+ u8 numNotifyCounters;
+ u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */
+} __attribute__ ((packed));
+typedef struct MRaidProductInfo megaRaidProductInfo;
+
+/********************************************
+ * Standard ENQUIRY
+ ********************************************/
+struct FC_ADP_INFO {
+ u8 MaxConcCmds; /* Max. concurrent commands supported. */
+ u8 RbldRate; /* Rebuild Rate. Varies from 0%-100% */
+ u8 MaxTargPerChan; /* Max. Targets supported per chan. */
+ u8 ChanPresent; /* No. of Chans present on this adapter. */
+ u8 FwVer[4]; /* Firmware version. */
+ u16 AgeOfFlash; /* No. of times FW has been downloaded. */
+ u8 ChipSetValue; /* Contents of 0xC0000832 */
+ u8 DramSize; /* In terms of MB */
+ u8 CacheFlushInterval; /* In terms of Seconds */
+ u8 BiosVersion[4];
+ u8 BoardType;
+ u8 sense_alert;
+ u8 write_config_count; /* Increase with evry configuration change */
+ u8 drive_inserted_count;/* Increase with every drive inserted */
+ u8 inserted_drive; /* Channel: Id of inserted drive */
+ u8 battery_status;
+ /*
+ BIT 0 : battery module missing
+ BIT 1 : VBAD
+ BIT 2 : temp high
+ BIT 3 : battery pack missing
+ BIT 4,5 : 00 - charge complete
+ 01 - fast charge in prog
+ 10 - fast charge fail
+ 11 - undefined
+ BIt 6 : counter > 1000
+ Bit 7 : undefined
+ */
+ u8 dec_fault_bus_info; /* was resvd */
+} __attribute__ ((packed));
+
+struct FC_LDRV_INFO {
+ u8 NumLDrv; /* No. of Log. Drvs configured. */
+ u8 recon_state[FC_MAX_LOGICAL_DRIVES / 8];
+ /* bit field for State of reconstruct */
+ u16 LDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8];
+ /* bit field Status of Long Operations. */
+
+ u32 LDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv. */
+ u8 LDrvProp[FC_MAX_LOGICAL_DRIVES];
+ u8 LDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives. */
+} __attribute__ ((packed));
+
+#define PREVSTAT_MASK 0xf0
+#define CURRSTAT_MASK 0x0f
+
+struct FC_PDRV_INFO {
+ u8 PDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys Drvs. */
+} __attribute__ ((packed));
+
+struct FC_AdapterInq {
+ struct FC_ADP_INFO AdpInfo;
+ struct FC_LDRV_INFO LogdrvInfo;
+ struct FC_PDRV_INFO PhysdrvInfo;
+} __attribute__ ((packed));
+
+typedef struct FC_AdapterInq mega_RAIDINQ_FC;
+
+/********************************************
+ * NOTIFICATION
+ ********************************************/
+
+#define MAX_NOTIFY_SIZE 0x80
+#define CUR_NOTIFY_SIZE sizeof(struct MegaRAID_Notify)
+
+/*
+ * Utilities declare this strcture size as ?? bytes. So more fields can
+ * be added in future.
+ */
+struct MegaRAID_Notify {
+ u32 globalCounter; /* Any change increments this counter */
+
+ u8 paramCounter; /* Indicates any params changed */
+ u8 paramId; /* Param modified - defined below */
+ u16 paramVal; /* New val of last param modified */
+
+ u8 writeConfigCounter; /* write config occurred */
+ u8 writeConfigRsvd[3];
+
+ u8 ldrvOpCounter; /* Indicates ldrv op started/completed */
+ u8 ldrvOpId; /* ldrv num */
+ u8 ldrvOpCmd; /* ldrv operation - defined below */
+ u8 ldrvOpStatus; /* status of the operation */
+
+ u8 ldrvStateCounter; /* Indicates change of ldrv state */
+ u8 ldrvStateId; /* ldrv num */
+ u8 ldrvStateNew; /* New state */
+ u8 ldrvStateOld; /* old state */
+
+ u8 pdrvStateCounter; /* Indicates change of ldrv state */
+ u8 pdrvStateId; /* pdrv id */
+ u8 pdrvStateNew; /* New state */
+ u8 pdrvStateOld; /* old state */
+
+ u8 pdrvFmtCounter; /* Indicates pdrv format started/over */
+ u8 pdrvFmtId; /* pdrv id */
+ u8 pdrvFmtVal; /* format started/over */
+ u8 pdrvFmtRsvd;
+
+ u8 targXferCounter; /* Indicates SCSI-2 Xfer rate change */
+ u8 targXferId; /* pdrv Id */
+ u8 targXferVal; /* new Xfer params of last pdrv */
+ u8 targXferRsvd;
+
+ u8 fcLoopIdChgCounter; /* Indicates loopid changed */
+ u8 fcLoopIdPdrvId; /* pdrv id */
+ u8 fcLoopId0; /* loopid on fc loop 0 */
+ u8 fcLoopId1; /* loopid on fc loop 1 */
+
+ u8 fcLoopStateCounter; /* Indicates loop state changed */
+ u8 fcLoopState0; /* state of fc loop 0 */
+ u8 fcLoopState1; /* state of fc loop 1 */
+ u8 fcLoopStateRsvd;
+} __attribute__ ((packed));
+
+/********************************************
+ * PARAM IDs in Notify struct
+ ********************************************/
+#define PARAM_RBLD_RATE 0x01
+ /*--------------------------------------
+ * Param val =
+ * byte 0: new rbld rate
+ *--------------------------------------*/
+#define PARAM_CACHE_FLUSH_INTERVAL 0x02
+ /*--------------------------------------
+ * Param val =
+ * byte 0: new cache flush interval
+ *--------------------------------------*/
+#define PARAM_SENSE_ALERT 0x03
+ /*--------------------------------------
+ * Param val =
+ * byte 0: last pdrv id causing chkcond
+ *--------------------------------------*/
+#define PARAM_DRIVE_INSERTED 0x04
+ /*--------------------------------------
+ * Param val =
+ * byte 0: last pdrv id inserted
+ *--------------------------------------*/
+#define PARAM_BATTERY_STATUS 0x05
+ /*--------------------------------------
+ * Param val =
+ * byte 0: battery status
+ *--------------------------------------*/
+
+/********************************************
+ * Ldrv operation cmd in Notify struct
+ ********************************************/
+#define LDRV_CMD_CHKCONSISTANCY 0x01
+#define LDRV_CMD_INITIALIZE 0x02
+#define LDRV_CMD_RECONSTRUCTION 0x03
+
+/********************************************
+ * Ldrv operation status in Notify struct
+ ********************************************/
+#define LDRV_OP_SUCCESS 0x00
+#define LDRV_OP_FAILED 0x01
+#define LDRV_OP_ABORTED 0x02
+#define LDRV_OP_CORRECTED 0x03
+#define LDRV_OP_STARTED 0x04
+
+/********************************************
+ * Raid Logical drive states.
+ ********************************************/
+#define RDRV_OFFLINE 0
+#define RDRV_DEGRADED 1
+#define RDRV_OPTIMAL 2
+#define RDRV_DELETED 3
+
+/*******************************************
+ * Physical drive states.
+ *******************************************/
+#define PDRV_UNCNF 0
+#define PDRV_ONLINE 3
+#define PDRV_FAILED 4
+#define PDRV_RBLD 5
+
+/*******************************************
+ * Formal val in Notify struct
+ *******************************************/
+#define PDRV_FMT_START 0x01
+#define PDRV_FMT_OVER 0x02
+
+/********************************************
+ * FC Loop State in Notify Struct
+ ********************************************/
+#define ENQ_FCLOOP_FAILED 0
+#define ENQ_FCLOOP_ACTIVE 1
+#define ENQ_FCLOOP_TRANSIENT 2
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#define M_RD_DMA_TYPE_NONE 0xFFFF
+#define M_RD_PTHRU_WITH_BULK_DATA 0x0001
+#define M_RD_PTHRU_WITH_SGLIST 0x0002
+#define M_RD_BULK_DATA_ONLY 0x0004
+#define M_RD_SGLIST_ONLY 0x0008
+#define M_RD_EPTHRU_WITH_BULK_DATA 0x0010
+#endif
+/********************************************
+ * ENQUIRY3
+ ********************************************/
+/*
+ * Utilities declare this strcture size as 1024 bytes. So more fields can
+ * be added in future.
+ */
+struct MegaRAID_Enquiry3 {
+ u32 dataSize; /* current size in bytes (not including resvd) */
+
+ struct MegaRAID_Notify notify;
+
+ u8 notifyRsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE];
+
+ u8 rbldRate; /* Rebuild rate (0% - 100%) */
+ u8 cacheFlushInterval; /* In terms of Seconds */
+ u8 senseAlert;
+ u8 driveInsertedCount; /* drive insertion count */
+
+ u8 batteryStatus;
+ u8 numLDrv; /* No. of Log Drives configured */
+ u8 reconState[FC_MAX_LOGICAL_DRIVES / 8]; /* State of reconstruct */
+ u16 lDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8]; /* log. Drv Status */
+
+ u32 lDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv */
+ u8 lDrvProp[FC_MAX_LOGICAL_DRIVES];
+ u8 lDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives */
+ u8 pDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys. Drvs. */
+ u16 physDrvFormat[FC_MAX_PHYSICAL_DEVICES / 16];
+
+ u8 targXfer[80]; /* phys device transfer rate */
+ u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */
+} __attribute__ ((packed));
+typedef struct MegaRAID_Enquiry3 mega_Enquiry3;
+
+/* Structures */
+typedef struct _mega_ADP_INFO {
+ u8 MaxConcCmds;
+ u8 RbldRate;
+ u8 MaxTargPerChan;
+ u8 ChanPresent;
+ u8 FwVer[4];
+ u16 AgeOfFlash;
+ u8 ChipSetValue;
+ u8 DramSize;
+ u8 CacheFlushInterval;
+ u8 BiosVer[4];
+ u8 resvd[7];
+} mega_ADP_INFO;
+
+typedef struct _mega_LDRV_INFO {
+ u8 NumLDrv;
+ u8 resvd[3];
+ u32 LDrvSize[MAX_LOGICAL_DRIVES];
+ u8 LDrvProp[MAX_LOGICAL_DRIVES];
+ u8 LDrvState[MAX_LOGICAL_DRIVES];
+} mega_LDRV_INFO;
+
+typedef struct _mega_PDRV_INFO {
+ u8 PDrvState[MAX_PHYSICAL_DRIVES];
+ u8 resvd;
+} mega_PDRV_INFO;
+
+/* RAID inquiry: Mailbox command 0x5*/
+typedef struct _mega_RAIDINQ {
+ mega_ADP_INFO AdpInfo;
+ mega_LDRV_INFO LogdrvInfo;
+ mega_PDRV_INFO PhysdrvInfo;
+} mega_RAIDINQ;
+
+/* Passthrough command: Mailbox command 0x3*/
+typedef struct mega_passthru {
+ u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ u8 ars:1;
+ u8 reserved:3;
+ u8 islogical:1;
+ u8 logdrv; /* if islogical == 1 */
+ u8 channel; /* if islogical == 0 */
+ u8 target; /* if islogical == 0 */
+ u8 queuetag; /* unused */
+ u8 queueaction; /* unused */
+ u8 cdb[MAX_CDB_LEN];
+ u8 cdblen;
+ u8 reqsenselen;
+ u8 reqsensearea[MAX_REQ_SENSE_LEN];
+ u8 numsgelements;
+ u8 scsistatus;
+ u32 dataxferaddr;
+ u32 dataxferlen;
+} mega_passthru;
+
+/*
+ * Extended passthru: support CDB > 10 bytes
+ */
+typedef struct {
+ u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ u8 ars:1;
+ u8 rsvd1:1;
+ u8 cd_rom:1;
+ u8 rsvd2:1;
+ u8 islogical:1;
+
+ u8 logdrv; /* if islogical == 1 */
+ u8 channel; /* if islogical == 0 */
+ u8 target; /* if islogical == 0 */
+
+ u8 queuetag; /* unused */
+ u8 queueaction; /* unused */
+
+ u8 cdblen;
+ u8 rsvd3;
+ u8 cdb[16];
+
+ u8 numsgelements;
+ u8 status;
+ u8 reqsenselen;
+ u8 reqsensearea[MAX_REQ_SENSE_LEN];
+ u8 rsvd4;
+
+ u32 dataxferaddr;
+ u32 dataxferlen;
+}mega_ext_passthru;
+
+struct _mega_mailbox {
+ /* 0x0 */ u8 cmd;
+ /* 0x1 */ u8 cmdid;
+ /* 0x2 */ u16 numsectors;
+ /* 0x4 */ u32 lba;
+ /* 0x8 */ u32 xferaddr;
+ /* 0xC */ u8 logdrv;
+ /* 0xD */ u8 numsgelements;
+ /* 0xE */ u8 resvd;
+ /* 0xF */ u8 busy;
+ /* 0x10 */ u8 numstatus;
+ /* 0x11 */ u8 status;
+ /* 0x12 */ u8 completed[46];
+ volatile u8 mraid_poll;
+ volatile u8 mraid_ack;
+ u8 pad[16]; /* for alignment purposes */
+} __attribute__ ((packed));
+typedef struct _mega_mailbox mega_mailbox;
+
+typedef struct {
+ u32 xferSegment_lo;
+ u32 xferSegment_hi;
+ mega_mailbox mailbox;
+} mega_mailbox64;
+
+typedef struct _mega_ioctl_mbox {
+ /* 0x0 */ u8 cmd;
+ /* 0x1 */ u8 cmdid;
+ /* 0x2 */ u8 channel;
+ /* 0x3 */ u8 param;
+ /* 0x4 */ u8 pad[4];
+ /* 0x8 */ u32 xferaddr;
+ /* 0xC */ u8 logdrv;
+ /* 0xD */ u8 numsgelements;
+ /* 0xE */ u8 resvd;
+ /* 0xF */ u8 busy;
+ /* 0x10 */ u8 numstatus;
+ /* 0x11 */ u8 status;
+ /* 0x12 */ u8 completed[46];
+ u8 mraid_poll;
+ u8 mraid_ack;
+ u8 malign[16];
+} mega_ioctl_mbox;
+
+typedef struct _mega_64sglist32 {
+ u64 address;
+ u32 length;
+} __attribute__ ((packed)) mega_64sglist;
+
+typedef struct _mega_sglist {
+ u32 address;
+ u32 length;
+} mega_sglist;
+
+/* Queued command data */
+typedef struct _mega_scb mega_scb;
+
+struct _mega_scb {
+ int idx;
+ u32 state;
+ u32 isrcount;
+ u8 mboxData[16];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ u32 dma_type;
+ dma_addr_t dma_h_bulkdata; /*Dma handle for bulk data transfter */
+ u32 dma_direction; /*Dma direction */
+ dma_addr_t dma_h_sgdata; /*Dma handle for the sglist structure */
+ dma_addr_t dma_h_sglist[MAX_SGLIST]; /*Dma handle for all SGL elements */
+ u8 sglist_count;
+ dma_addr_t dma_sghandle64;
+ dma_addr_t dma_passthruhandle64;
+ dma_addr_t dma_ext_passthruhandle64;
+ dma_addr_t dma_bounce_buffer;
+ u8 *bounce_buffer;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ mega_passthru *pthru;
+ mega_ext_passthru *epthru;
+#else
+ mega_passthru pthru;
+ mega_ext_passthru epthru;
+#endif
+
+ Scsi_Cmnd *SCpnt;
+ mega_sglist *sgList;
+ mega_64sglist *sg64List;
+#if 0
+ struct semaphore ioctl_sem;
+#endif
+ void *buff_ptr;
+ u32 iDataSize;
+ mega_scb *next;
+};
+
+/* internal locking by the queue manipulting routines */
+#define INTERNAL_LOCK 0
+/* external locking by the queue manipulting routines */
+#define EXTERNAL_LOCK 1
+#define NO_LOCK 2
+#define INTR_ENB 0 /* do not disable interrupt while manipulating */
+#define INTR_DIS 1 /* disable interrupt while manipulating */
+
+#define NVIRT_CHAN 4 /* # of virtual channels to represent 60 logical
+ drives */
+
+/* Per-controller data */
+typedef struct _mega_host_config {
+ u8 numldrv;
+ u32 flag;
+
+#ifdef __LP64__
+ u64 base;
+#else
+ u32 base;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ dma_addr_t dma_handle64, adjdmahandle64;
+ struct pci_dev *dev;
+#endif
+
+ mega_scb *qFreeH;
+ mega_scb *qFreeT;
+ spinlock_t lock_free;
+
+ mega_scb *qPendingH;
+ mega_scb *qPendingT;
+ spinlock_t lock_pend;
+
+ Scsi_Cmnd *qCompletedH;
+ Scsi_Cmnd *qCompletedT;
+ spinlock_t lock_scsicmd;
+
+ u32 qFcnt;
+ u32 qPcnt;
+ u32 qCcnt;
+
+ unsigned long nReads[FC_MAX_LOGICAL_DRIVES];
+ unsigned long nReadBlocks[FC_MAX_LOGICAL_DRIVES];
+ unsigned long nWrites[FC_MAX_LOGICAL_DRIVES];
+ unsigned long nWriteBlocks[FC_MAX_LOGICAL_DRIVES];
+ unsigned long nInterrupts;
+ /* Host adapter parameters */
+ u8 fwVer[7];
+ u8 biosVer[7];
+
+ struct Scsi_Host *host;
+
+ volatile mega_mailbox64 *mbox64; /* ptr to beginning of 64-bit mailbox */
+ volatile mega_mailbox *mbox; /* ptr to beginning of standard mailbox */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+/* ptr to beginning of standard mailbox */
+ volatile mega_mailbox64 *mailbox64ptr;
+#else
+ volatile mega_mailbox64 mailbox64;
+#endif
+
+ volatile u8 mega_buffer[2 * 1024L];
+ volatile megaRaidProductInfo productInfo;
+
+ u8 max_cmds;
+ mega_scb scbList[MAX_COMMANDS];
+
+#if XENO_KILLED
+#define PROCBUFSIZE 4096
+ char procbuf[PROCBUFSIZE];
+ int procidx;
+ struct proc_dir_entry *controller_proc_dir_entry;
+ struct proc_dir_entry *proc_read, *proc_stat, *proc_status, *proc_mbox;
+#endif
+
+ int support_ext_cdb;
+
+ u8 boot_ldrv_enabled; /* boot from logical drive */
+ u8 boot_ldrv; /* boot logical drive */
+ u8 boot_pdrv_enabled; /* boot from physical drive */
+ u8 boot_pdrv_ch; /* boot physical drive channel */
+ u8 boot_pdrv_tgt; /* boot physical drive target */
+
+ int support_random_del; /* Do we support random deletion of logdrvs */
+ int read_ldidmap; /* set after logical drive deltion. The logical
+ drive number must be read from the map */
+ int quiescent; /* a stage reached when delete logical drive needs to
+ be done. Stop sending requests to the hba till
+ delete operation is completed */
+
+ mega_scb *int_qh; /* commands are queued in the internal queue */
+ mega_scb *int_qt; /* while the hba is quiescent */
+ int int_qlen;
+ char logdrv_chan[MAX_CHANNEL+NVIRT_CHAN]; /* logical drive are on
+ what channels. */
+ int mega_ch_class;
+} mega_host_config;
+
+typedef struct _driver_info {
+ int size;
+ ulong version;
+} mega_driver_info;
+
+/*
+ * User ioctl structure.
+ * This structure will be used for Traditional Method ioctl interface
+ * commands (M_RD_IOCTL_CMD),Alternate Buffer Method (M_RD_IOCTL_CMD_NEW)
+ * ioctl commands and the Driver ioctls(M_RD_DRIVER_IOCTL_INTERFACE).
+ * The Driver ioctl interface handles the commands at
+ * the driver level, without being sent to the card.
+ */
+#define MEGADEVIOC 0x84
+
+/* system call imposed limit. Change accordingly */
+#define IOCTL_MAX_DATALEN 4096
+
+#pragma pack(1)
+struct uioctl_t {
+ u32 inlen;
+ u32 outlen;
+ union {
+ u8 fca[16];
+ struct {
+ u8 opcode;
+ u8 subopcode;
+ u16 adapno;
+#if BITS_PER_LONG == 32
+ u8 *buffer;
+ u8 pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ u8 *buffer;
+#endif
+ u32 length;
+ } fcs;
+ } ui;
+ u8 mbox[18]; /* 16 bytes + 2 status bytes */
+ mega_passthru pthru;
+#if BITS_PER_LONG == 32
+ char *data; /* buffer <= 4096 for 0x80 commands */
+ char pad[4];
+#endif
+#if BITS_PER_LONG == 64
+ char *data;
+#endif
+};
+#pragma pack()
+
+/*
+ * struct mcontroller is used to pass information about the controllers in the
+ * system. Its upto the application how to use the information. We are passing
+ * as much info about the cards as possible and useful. Before issuing the
+ * call to find information about the cards, the applicaiton needs to issue a
+ * ioctl first to find out the number of controllers in the system.
+ */
+#define MAX_CONTROLLERS 32
+
+struct mcontroller {
+ u64 base;
+ u8 irq;
+ u8 numldrv;
+ u8 pcibus;
+ u16 pcidev;
+ u8 pcifun;
+ u16 pciid;
+ u16 pcivendor;
+ u8 pcislot;
+ u32 uid;
+};
+
+struct mbox_passthru {
+ u8 cmd;
+ u8 cmdid;
+ u16 pad1;
+ u32 pad2;
+ u32 dataxferaddr;
+ u8 pad3;
+ u8 pad4;
+ u8 rsvd;
+ u8 mboxbusy;
+ u8 nstatus;
+ u8 status;
+};
+
+/*
+ * Defines for Driver IOCTL interface, Op-code:M_RD_DRIVER_IOCTL_INTERFACE
+ */
+#define MEGAIOC_MAGIC 'm'
+#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0) /* Mega IOCTL command */
+
+#define MEGAIOC_QNADAP 'm' /* Query # of adapters */
+#define MEGAIOC_QDRVRVER 'e' /* Query driver version */
+#define MEGAIOC_QADAPINFO 'g' /* Query adapter information */
+#define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) )
+#define GETADAP(mkadap) ( (mkadap) ^ MEGAIOC_MAGIC << 8 )
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */
+extern struct proc_dir_entry proc_scsi_megaraid;
+#endif
+
+/* For Host Re-Ordering */
+#define MAX_CONTROLLERS 32
+
+struct mega_hbas {
+ int is_bios_enabled;
+ mega_host_config *hostdata_addr;
+};
+
+#define IS_BIOS_ENABLED 0x62
+#define GET_BIOS 0x01
+#define CHNL_CLASS 0xA9
+#define GET_CHNL_CLASS 0x00
+#define SET_CHNL_CLASS 0x01
+#define CH_RAID 0x01
+#define CH_SCSI 0x00
+
+
+#define BIOS_PVT_DATA 0x40
+#define GET_BIOS_PVT_DATA 0x00
+
+#pragma pack(1)
+struct private_bios_data {
+ u8 geometry:4; /*
+ * bits 0-3 - BIOS geometry
+ * 0x0001 - 1GB
+ * 0x0010 - 2GB
+ * 0x1000 - 8GB
+ * Others values are invalid
+ */
+ u8 unused:4; /* bits 4-7 are unused */
+ u8 boot_drv; /*
+ * logical/physical drive set as boot drive
+ * 0..7 - for 8LD cards
+ * 0..39 - for 40LD cards
+ */
+ u8 rsvd[12];
+ u16 cksum; /* 0-(sum of first 13 bytes of this structure) */
+};
+#pragma pack()
+
+/*
+ * Command for random deletion of logical drives
+ */
+#define FC_DEL_LOGDRV 0xA4 /* f/w command */
+#define OP_SUP_DEL_LOGDRV 0x2A /* is feature supported */
+#define OP_GET_LDID_MAP 0x18 /* get logdrv id and logdrv number map */
+#define OP_DEL_LOGDRV 0x1C /* delete logical drive */
+
+/*================================================================
+ *
+ * Function prototypes
+ *
+ *================================================================
+ */
+const char *megaraid_info (struct Scsi_Host *);
+int megaraid_detect (Scsi_Host_Template *);
+int megaraid_release (struct Scsi_Host *);
+int megaraid_command (Scsi_Cmnd *);
+int megaraid_abort (Scsi_Cmnd *);
+int megaraid_reset (Scsi_Cmnd *, unsigned int);
+int megaraid_queue (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+int megaraid_biosparam (Disk *, kdev_t, int *);
+#if 0
+int megaraid_proc_info (char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout);
+#endif
+static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData,
+ mega_scb * scb, int intr);
+static int mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb,
+ u32 * buffer, u32 * length);
+static int mega_busyWaitMbox (mega_host_config *);
+static int mega_runpendq (mega_host_config *);
+static void mega_rundoneq (mega_host_config *);
+static void mega_cmd_done (mega_host_config *, mega_scb *, int);
+static inline void mega_freeSgList (mega_host_config * megaCfg);
+static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry,
+ mega_Enquiry3 * enquiry3,
+ megaRaidProductInfo * productInfo);
+
+static int megaraid_reboot_notify (struct notifier_block *,
+ unsigned long, void *);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt);
+static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb,
+ mega_ioctl_mbox * mbox);
+#endif
+
+static int megadev_open (struct inode *, struct file *);
+static int megadev_ioctl_entry (struct inode *, struct file *,
+ unsigned int, unsigned long);
+static int megadev_ioctl (struct inode *, struct file *,
+ unsigned int, unsigned long);
+static mega_scb *megadev_doioctl (mega_host_config *, Scsi_Cmnd *);
+static int megadev_close (struct inode *, struct file *);
+static void megadev_ioctl_done (Scsi_Cmnd *);
+static int mega_init_scb (mega_host_config *);
+static void enq_scb_freelist (mega_host_config *, mega_scb *,
+ int lock, int intr);
+
+static int mega_is_bios_enabled (mega_host_config *);
+static void mega_reorder_hosts (void);
+static void mega_swap_hosts (struct Scsi_Host *, struct Scsi_Host *);
+
+#if 0
+static void mega_create_proc_entry (int index, struct proc_dir_entry *);
+#endif
+static int mega_support_ext_cdb(mega_host_config *);
+static mega_passthru* mega_prepare_passthru(mega_host_config *, mega_scb *,
+ Scsi_Cmnd *, int, int);
+static mega_ext_passthru* mega_prepare_extpassthru(mega_host_config *,
+ mega_scb *, Scsi_Cmnd *, int, int);
+static void mega_enum_raid_scsi(mega_host_config *);
+static int mega_partsize(Disk *, kdev_t, int *);
+static void mega_get_boot_drv(mega_host_config *);
+static int mega_get_ldrv_num(mega_host_config *, Scsi_Cmnd *, int);
+static int mega_support_random_del(mega_host_config *);
+static int mega_del_logdrv(mega_host_config *, int);
+static int mega_do_del_logdrv(mega_host_config *, int);
+
+#endif
+
+/* vi: set ts=4: */